[
  {
    "path": ".gitignore",
    "content": "*.data\n*.pyc\n*.npy\n*.npz\n*.pth\n*.bak\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 2.8.3)\nproject(cerebro)\n\n# Catkin components\nfind_package(catkin REQUIRED COMPONENTS\n  cv_bridge\n  image_transport\n  roscpp\n  rospy\n  std_msgs\n  message_generation\n)\n\n\n\n## System dependencies are found with CMake's conventions\n# find_package(Boost REQUIRED COMPONENTS system)\nset( CMAKE_CXX_STANDARD 11 )\nfind_package(Threads)\nfind_package(Eigen3 REQUIRED)\nfind_package(Ceres REQUIRED)\nfind_package(OpenCV 3 REQUIRED)\n#set( CMAKE_CXX_FLAGS \"-fpermissive -std=c++11 -O3 -Wall -Wextra -pedantic\" )\n\n# Removing the dependence on theia for the production code.\n# If you want to compile unit tests, some of those need theia-sfm as dependence.\nfind_package(Theia REQUIRED)\ninclude_directories(${THEIA_INCLUDE_DIRS})\n\nfind_package(Boost REQUIRED COMPONENTS filesystem program_options system)\ninclude_directories(${Boost_INCLUDE_DIRS})\n\n\n#----------START gperftools\n# install : sudo apt-get install google-perftools libgoogle-perftools-dev\n# get cmake file: https://raw.githubusercontent.com/vast-io/vast/master/cmake/FindGperftools.cmake\n# save to '/usr/local/lib/cmake'\n#list(APPEND CMAKE_MODULE_PATH \"/usr/local/lib/cmake\")\n#find_package(Gperftools REQUIRED)\n#message( \"----- GPERF\")\n#message( \"----- \"${GPERFTOOLS_LIBRARIES} )\nset (CMAKE_CXX_FLAGS \" -Wl,-no-as-needed\")\n#set(CMAKE_BUILD_TYPE Debug)\nset(CMAKE_BUILD_TYPE Release)\n#set(CMAKE_BUILD_TYPE RelWithDebInfo)\n\n#----------END GPERFTOOLS\n\noption(COMPILE_UNIT_TEST \"Compile unit tests\" OFF) #OFF by default. catkin_make -DCOMPILE_UNIT_TEST=ON will compile it. OFF will skip it\n\n\n################################################\n## Declare ROS messages, services and actions ##\n################################################\n\n## To declare and build messages, services or actions from within this\n## package, follow these steps:\n## * Let MSG_DEP_SET be the set of packages whose message types you use in\n##   your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).\n## * In the file package.xml:\n##   * add a build_depend tag for \"message_generation\"\n##   * add a build_depend and a run_depend tag for each package in MSG_DEP_SET\n##   * If MSG_DEP_SET isn't empty the following dependency has been pulled in\n##     but can be declared for certainty nonetheless:\n##     * add a run_depend tag for \"message_runtime\"\n## * In this file (CMakeLists.txt):\n##   * add \"message_generation\" and every package in MSG_DEP_SET to\n##     find_package(catkin REQUIRED COMPONENTS ...)\n##   * add \"message_runtime\" and every package in MSG_DEP_SET to\n##     catkin_package(CATKIN_DEPENDS ...)\n##   * uncomment the add_*_files sections below as needed\n##     and list every .msg/.srv/.action file to be processed\n##   * uncomment the generate_messages entry below\n##   * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)\n\n## Generate messages in the 'msg' folder\nadd_message_files(\n  FILES\n    LoopEdge.msg\n)\n\n## Generate services in the 'srv' folder\nadd_service_files(\n   FILES\n   WholeImageDescriptorCompute.srv\n )\n\n\n## Generate added messages and services with any dependencies listed here\n generate_messages(\n   DEPENDENCIES\n   std_msgs\n   sensor_msgs\n )\n\n\n\n###################################\n## catkin specific configuration ##\n###################################\n## The catkin_package macro generates cmake config files for your package\n## Declare things to be passed to dependent projects\n## INCLUDE_DIRS: uncomment this if you package contains header files\n## LIBRARIES: libraries you create in this project that dependent projects also need\n## CATKIN_DEPENDS: catkin_packages dependent projects also need\n## DEPENDS: system dependencies of this project that dependent projects also need\ncatkin_package(\n  INCLUDE_DIRS include\n#  LIBRARIES cerebro\n#  CATKIN_DEPENDS cv_bridge image_transport roscpp rospy std_msgs\n#  DEPENDS system_lib\nCATKIN_DEPENDS message_runtime\n)\n\n###########\n## Build ##\n###########\n\n## Specify additional locations of header files\n## Your package locations should be listed before other locations\ninclude_directories(include)\ninclude_directories(\n  ${catkin_INCLUDE_DIRS}\n  ${CERES_INCLUDE_DIRS}\n  ${EIGEN3_INCLUDE_DIRS}\n  ${OpenCV_INCLUDE_DIRS}\n)\n\n\ninclude_directories(\n    src/utils/camodocal/include\n    src/utils/\n    )\nFILE(GLOB CamodocalCameraModelSources\n        src/utils/camodocal/src/chessboard/Chessboard.cc\n        src/utils/camodocal/src/calib/CameraCalibration.cc\n        src/utils/camodocal/src/camera_models/Camera.cc\n        src/utils/camodocal/src/camera_models/CameraFactory.cc\n        src/utils/camodocal/src/camera_models/CostFunctionFactory.cc\n        src/utils/camodocal/src/camera_models/PinholeCamera.cc\n        src/utils/camodocal/src/camera_models/CataCamera.cc\n        src/utils/camodocal/src/camera_models/EquidistantCamera.cc\n        src/utils/camodocal/src/camera_models/ScaramuzzaCamera.cc\n        src/utils/camodocal/src/sparse_graph/Transform.cc\n        src/utils/camodocal/src/gpl/gpl.cc\n        src/utils/camodocal/src/gpl/EigenQuaternionParameterization.cc\n    )\n\nFILE( GLOB KuseUtilsSources\n        src/utils/PoseManipUtils.cpp\n        src/utils/RosMarkerUtils.cpp\n        src/utils/RawFileIO.cpp\n        src/utils/MiscUtils.cpp\n        src/utils/Plot2Mat.cpp\n        src/utils/CameraGeometry.cpp\n        src/utils/PointFeatureMatching.cpp\n        src/utils/GMSMatcher/gms_matcher.cpp\n)\n\n\n## Declare a C++ executable\n\nadd_executable(\n    cerebro_node\n        src/cerebro_node.cpp\n        src/PinholeCamera.cpp\n        src/DataManager.cpp\n        src/DataNode.cpp\n        src/Cerebro.cpp\n        src/Visualization.cpp\n        src/ProcessedLoopCandidate.cpp\n        src/DlsPnpWithRansac.cpp\n        src/HypothesisManager.cpp\n        src/ImageDataManager.cpp\n        ${KuseUtilsSources}\n        ${CamodocalCameraModelSources}\n)\n\n# this is needed to indicate this this executable depends on the messages of the pkg.\nadd_dependencies(cerebro_node cerebro_generate_messages_cpp)\n\n\ntarget_link_libraries(cerebro_node\n  ${catkin_LIBRARIES}\n  ${OpenCV_LIBRARIES}\n  ${CMAKE_THREAD_LIBS_INIT}\n  ${CERES_LIBRARIES}\n  ${Boost_LIBRARIES}\n  ${THEIA_LIBRARIES}\n\n  #${GPERFTOOLS_LIBRARIES}\n  #/usr/local/lib/libfaiss.so\n)\n\nif(COMPILE_UNIT_TEST)\n    #add some compilation flags\n\n\n    ## Unit Tests\n    add_executable (\n        unittest_termcolor\n            src/unittest/unittest_termcolor.cpp\n    )\n\n    add_executable (\n        unittest_rosservice_client\n            src/unittest/unittest_rosservice_client.cpp\n    )\n\n    add_executable (\n        unittest_camodocal_proj\n            src/unittest/unittest_camodocal_proj.cpp\n            ${KuseUtilsSources}\n            ${CamodocalCameraModelSources}\n    )\n\n\n    add_executable (\n        unittest_camera_geom_class_usage\n            src/unittest/unittest_camera_geom_class_usage.cpp\n            ${KuseUtilsSources}\n            ${CamodocalCameraModelSources}\n    )\n\n\n    add_executable (\n        unittest_theia\n            src/unittest/unittest_theia.cpp\n            ${KuseUtilsSources}\n            ${CamodocalCameraModelSources}\n    )\n\n    add_executable (\n        unittest_pose_tester\n            src/unittest/unittest_pose_tester.cpp\n            ${KuseUtilsSources}\n            ${CamodocalCameraModelSources}\n            src/DlsPnpWithRansac.cpp\n    )\n\n\n    add_executable (\n        unittest_plot2mat\n            src/unittest/unittest_plot2mat.cpp\n            src/utils/Plot2Mat.cpp\n    )\n\n\n    target_link_libraries(unittest_rosservice_client\n      ${catkin_LIBRARIES}\n      ${OpenCV_LIBRARIES}\n      ${CMAKE_THREAD_LIBS_INIT}\n      ${CERES_LIBRARIES}\n    )\n\n\n    target_link_libraries(unittest_camodocal_proj\n      ${catkin_LIBRARIES}\n      ${OpenCV_LIBRARIES}\n      ${CMAKE_THREAD_LIBS_INIT}\n      ${CERES_LIBRARIES}\n    )\n\n    target_link_libraries(unittest_camera_geom_class_usage\n      ${catkin_LIBRARIES}\n      ${OpenCV_LIBRARIES}\n      ${CMAKE_THREAD_LIBS_INIT}\n      ${CERES_LIBRARIES}\n    )\n\n\n\n    target_link_libraries(unittest_theia\n      ${catkin_LIBRARIES}\n      ${OpenCV_LIBRARIES}\n      ${CMAKE_THREAD_LIBS_INIT}\n      ${CERES_LIBRARIES}\n      ${THEIA_LIBRARIES}\n    )\n\n    target_link_libraries(unittest_pose_tester\n      ${catkin_LIBRARIES}\n      ${OpenCV_LIBRARIES}\n      ${CMAKE_THREAD_LIBS_INIT}\n      ${CERES_LIBRARIES}\n      ${THEIA_LIBRARIES}\n    )\n\n    target_link_libraries(unittest_plot2mat\n      ${catkin_LIBRARIES}\n      ${OpenCV_LIBRARIES}\n    )\n\n\nelse()\n    #add some other compilation flags\nendif(COMPILE_UNIT_TEST)\nunset(COMPILE_UNIT_TEST CACHE) # <---- this is the important!!\n\n\n\n#############\n## Install ##\n#############\n\n# all install targets should use catkin DESTINATION variables\n# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html\n\n## Mark executable scripts (Python etc.) for installation\n## in contrast to setup.py, you can choose the destination\n# install(PROGRAMS\n#   scripts/my_python_script\n#   DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}\n# )\n\n## Mark executables and/or libraries for installation\n# install(TARGETS cerebro cerebro_node\n#   ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}\n#   LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}\n#   RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}\n# )\n\n## Mark cpp header files for installation\n# install(DIRECTORY include/${PROJECT_NAME}/\n#   DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}\n#   FILES_MATCHING PATTERN \"*.h\"\n#   PATTERN \".svn\" EXCLUDE\n# )\n\n## Mark other files for installation (e.g. launch and bag files, etc.)\n# install(FILES\n#   # myfile1\n#   # myfile2\n#   DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}\n# )\n\n#############\n## Testing ##\n#############\n\n## Add gtest based cpp test target and link libraries\n# catkin_add_gtest(${PROJECT_NAME}-test test/test_cerebro.cpp)\n# if(TARGET ${PROJECT_NAME}-test)\n#   target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})\n# endif()\n\n## Add folders to be run by python nosetests\n# catkin_add_nosetests(test)\n"
  },
  {
    "path": "README.md",
    "content": "# VINS-Fusion with Cerebro\nThis is the cerebro module for VINS-Fusion. The aim of this project\nis better loop detection and recover from kidnap. The cerebro node connects to\nthe vins_estimator nodes of VINS-Fusion\n(with ros interface). The DataManager class handles\nall the incoming data. Visualization handles all the visualization.\nCerebro class handles the loop closure intelligence part. It publishes a LoopMsg\nwhich contains timestamps of the identified loopcandidate along with the\ncomputed relative pose between the pair. The pose computation needs a stereo pair\nfor reliable pose computation.\nThis is a multi-threaded object oriented implementation and I observe a CPU load factor\nof about 2.0. A separate node handles pose graph solver (it is in [github-repo](https://github.com/mpkuse/solve_keyframe_pose_graph) ).\n\nThis repo also support for saving the trajectories to file and later\nloading it from file for relocalization. See [launch/realsense_vinsfusion_ondrone_teach.launch](launch/realsense_vinsfusion_ondrone_teach.launch) on how to do it.\n\nIf you use this work in your research, please cite:\nManohar Kuse and Shaojie Shen, *“Learning Whole-Image Descriptors for Real-time Loop Detection and Kidnap Recovery under Large Viewpoint Difference“*, https://arxiv.org/abs/1904.06962\n\n## Highlight Video\n[![IMAGE ALT TEXT](http://img.youtube.com/vi/lDzDHZkInos/0.jpg)](http://www.youtube.com/watch?v=lDzDHZkInos \"Video Title\")\n\n\n## MyntEye Demo (Using VINS-Fusion as Odometry Estimator)\nWe showcase the kidnap recovery and relocalization.\n[![IMAGE ALT TEXT](http://img.youtube.com/vi/3YQF4_v7AEg/0.jpg)](http://www.youtube.com/watch?v=3YQF4_v7AEg \"Video Title\")\n\n\n[![IMAGE ALT TEXT](http://img.youtube.com/vi/sTd_rZdW4DQ/0.jpg)](http://www.youtube.com/watch?v=sTd_rZdW4DQ \"Video Title\")\n\n\n## In Plane Rotation Test (Uses Realsense Camera)\n[![IMAGE ALT TEXT](http://img.youtube.com/vi/8bsRCNF2rnA/0.jpg)](http://www.youtube.com/watch?v=8bsRCNF2rnA \"Video Title\")\n\n## Relocalization from Previously Constructed Trajectories.\nWe show the accurary of the system by plotting a previously constructed surfel map (in gray color)\nand the newly constructed surfel map (rainbox colored). We can observe that the boxes for example in the environment overlap. Similarly other objects in the seen also overlap. \n[![IMAGE ALT TEXT](http://img.youtube.com/vi/OViEEB3rINo/0.jpg)](http://www.youtube.com/watch?v=OViEEB3rINo \"Video Title\")\n\n## MyntEye Demo (Using VINS-Mono as Odometry Estimator)\n[![IMAGE ALT TEXT](http://img.youtube.com/vi/KDRo9LpL6Hs/0.jpg)](http://www.youtube.com/watch?v=KDRo9LpL6Hs \"Video Title\")\n\n[![IMAGE ALT TEXT](http://img.youtube.com/vi/XvoCrLFq99I/0.jpg)](http://www.youtube.com/watch?v=XvoCrLFq99I \"Video Title\")\n\n\n\n## EuRoC MAV Dataset live merge MH-01, ... MH-05.\n[![IMAGE ALT TEXT](http://img.youtube.com/vi/mnnoAlAIsN8/0.jpg)](http://www.youtube.com/watch?v=mnnoAlAIsN8 \"Video Title\")\n\n\n## EuRoC MAV Dataset live merge V1_01, V1_02, V1_03, V2_01, V2_02\n\n[![IMAGE ALT TEXT](http://img.youtube.com/vi/rIaANkd74cQ/0.jpg)](http://www.youtube.com/watch?v=rIaANkd74cQ \"Video Title\")\n\n\n\n\nFor more demonstration, have a look at my [youtube playlist](https://www.youtube.com/playlist?list=PLWyydx20vdPzs5VVhZu0TGsReT7U17Fxp)\n\n\n\n## Visual-Inertial Datasets\n- [ETHZ EuroC Dataset](https://projects.asl.ethz.ch/datasets/doku.php?id=kmavvisualinertialdatasets)\n- [TUM Visual Inertial Dataset](https://vision.in.tum.de/data/datasets/visual-inertial-dataset)\n- UPenn - [Penncosyvio](https://github.com/daniilidis-group/penncosyvio)\n- [ADVIO](https://github.com/AaltoVision/ADVIO) ARKIT, Tango logging.\n- Our MyntEye (Stereo+IMU) [One-drive-link](https://hkustconnect-my.sharepoint.com/:f:/g/personal/mpkuse_connect_ust_hk/EkTisuLkXLFBs_WHYkxoH2oBeVIkdLc3-5a_t1J9c_4wkg?e=h9cifx)\n\n## How to run - Docker\nI highly recommend the already deployed packages with docker.\nRun the roscore on your host pc and all the packages run inside\nof docker container. rviz runs on the host pc.\n\nI assume you have a PC with a graphics card and cuda9 working smoothly\nand nvidia-docker installed.\n```\n$(host) export ROS_HOSTNAME=`hostname`\n$(host) roscore\n# assume that host has the ip address 172.17.0.1 in docker-network aka docker0\n$(host) docker run --runtime=nvidia -it --rm \\\n        --add-host `hostname`:172.17.0.1 \\\n        --env ROS_MASTER_URI=http://`hostname`:11311/ \\\n        --env CUDA_VISIBLE_DEVICES=0 \\\n        --hostname happy_go \\\n        --name happy_go  \\\n        mpkuse/kusevisionkit:ros-kinetic-vins-tf-faiss bash\n$(host) rviz # inside rviz open config cerebro/config/good-viz.rviz. If you open rviz in a new tab you might need to do set ROS_HOSTNAME again.\n$(docker) roslaunch cerebro mynteye_vinsfusion.launch\n            OR\n$(docker) roslaunch cerebro euroc_vinsfusion.launch\n$(host) rosbag play 1.bag\n```\n\n\n\nIf you are unfamiliar with docker, you may want to read [my blog post](https://kusemanohar.wordpress.com/2018/10/03/docker-for-computer-vision-researchers/)\non using docker for computer vision researchers.\nYou might want to have a look at my test ros-package to ensure things work with docker [docker_ros_test](https://github.com/mpkuse/docker_ros_test).\nList of all my docker-images can be found [here](https://hub.docker.com/r/mpkuse/kusevisionkit).\nOther docker images that work:\n- mpkuse/kusevisionkit:ros-kinetic-vins-tf-faiss (preferred)\n- mpkuse/kusevisionkit:vins-kidnap\n\n\n## How to compile (from scratch)\nYou will need a) VINS-Fusion (with modification for reset by mpkuse), b) cerebro\nand c) solve_keyframe_pose_graph. Besure to setup a `catkin_ws` and make sure\nyour ROS works correctly.\n\n### Dependencies\n- ROS Kinetic\n- Eigen3\n- Ceres\n- OpenCV3 (should also work with 2.4 (not tested), 3.3 (tested Ok) and 3.4 (tested ok))\n- [Theia-sfm](http://theia-sfm.org/). *As of May2019 dependence on theia-sfm has been eliminated. The code is able to compile even when you do not have Theia-sfm installed. The pose estimation PNP/P3P has been implemented from scratch using ceres. *\n    - [OpenImageIO](https://github.com/OpenImageIO/oiio) (Release-1.7.6RC1) <br/>\n    - [RocksDB](https://github.com/facebook/rocksdb) (v5.9.2) <br/>\n    - OpenExr <br/>\n- Tensorflow (tested on 1.11.0)\n- Keras (atleast 2.2.4), I noticed issues in jsonmodel with 2.2.2, 2.2.2 still works though!\n\n\n### Get VINS-Fusion Working\nI recommend you use my fork of VINS-Fusion, in which I have fixed some bugs\nand added mechanism for reseting the VINS.\n```\ncd catkin_ws/src\n#git clone https://github.com/HKUST-Aerial-Robotics/VINS-Fusion.git\ngit clone https://github.com/mpkuse/VINS-Fusion\ncd ../\ncatkin_make\nsource catkin_ws/devel/setup.bash\n```\n\nMake sure your vins-fusion can compile and run correctly. See vins-fusion github repo\nfor the latest information on prerequisites and compilation instructions.\nFor compatibility I recommend using my fork of vins-mono/vins-fusion. Some minor\nmodifications have been made by me for working with kidnap cases.\n\n### Cerebro\n```\ncd catkin_ws/src/\ngit clone https://github.com/mpkuse/cerebro\ncd ../\ncatkin_make\n```\n\nThis has 2 exectables. **a)** ros server that takes as input an image and\nreturns a image descriptor. **b)** cerebro_node, this\nfinds the loop candidates and computes the relative poses. I have also\nincluded my trained model (about 4 MB) in this package (located scripts/keras.model). The pose computation\nuses the stereo pair in this node. This node publishes the loopcandidate's relative pose\nwhich is expected to be consumed the pose-graph solver.\n\nIf you wish to train your own model, you may use  [my learning code here](https://github.com/mpkuse/cartwheel_train).\n\n**Threads in cerebro_node:**\n- *Main Thread* : ros-callbacks\n- *data_association_th* : Sync the incoming image data and incoming data vins_estimator.\n- *desc_th* : Consumes the images to produce whole-image-descriptors.\n- *dot_product_th* : Dot product of current image descriptor with all the previous ones.\n- *loopcandidate_consumer_th* : Computes the relative pose at the loopcandidates. Publishes the loopEdge.\n- *kidnap_th* : Identifies kidnap. If kidnap publishes the reset signals for vins_estimator.\n- *viz_th* : Publishes the image-pair, and more things for debugging and analysis.\n- *dm_cleanup_th* : Deallocate/Store to file images to reduce RAM consumption.\n\n\n**Nvidia TX2**: Often times for live run, you might want to run the\nros-server on a Nvidia-TX2. I recommend compiling tensorflow from scratch.\nThe thing is prebuilt binaries may not be compatible with the version\nof CUDA and CUDNN on your device. Also some binaries may not be\ncompatible to arm (could likely be built for x86). Before you can\ncompile tensorflow you need java, bazel. See this [gist](https://gist.github.com/vellamike/7c26158c93e89ef155c1cc953bbba956). Also tools and repos from [jetsonhacks](https://github.com/jetsonhacks)\nmight come in handy.\n\n### Pose Graph Solver\nUse my pose graph solver, [github-repo](https://github.com/mpkuse/solve_keyframe_pose_graph).\nThe differences between this implementation and the\noriginal from VINS-Fusion is that mine can handle kidnap cases,\nhandles multiple world co-ordinate frames and it\nuses a switch-constraint formulation of the pose-graph problem.\n```\ncd catkin_ws/src/\ngit clone https://github.com/mpkuse/solve_keyframe_pose_graph\ncd ../\ncatkin_make\n```\n\n**Threads:**\n- *Main thread* : ros-callbacks for odometry poses (from vins_estimator) and LoopMsg (from cerebro).\n- *SLAM* : Monitors the node-poses and loop-edges, on new loop-edges constructs and solves the pose-graph optimization problem.\n- *th4* : Publish latest odometry.\n- *th5* : Display image to visualize disjoint set datastructure.\n- *th6* : Publish corrected poses, Different color for nodes in different co-ordinate systems.\n\n\n### vins_mono_debug_pkg (optional, needed only if you wish to debug vins-mono/vins-fusion)\nWith cerebro node it is possible to live run the vins and make it log all the\ndetails to file for further analysis/debugging. This might be useful\nfor researchers and other Ph.D. student to help VINS-Fusion improve further.\nsee [github/mpkuse/vins_mono](https://github.com/mpkuse/vins_mono_debug_pkg).\nIt basically contains unit tests and some standalone tools which might come in handy.\nIf you are looking to help improve VINS-fusion or cerebro also look at 'Development Guidelines'.\n\n## How to run the Full System\n```\nroslaunch cerebro mynteye_vinsfusion.launch\n```\nYou can get some of my bag files collected with the mynteye camera HERE. More\nexample launch files in folder `launch`, all the config files which contains\ncalibration info are in folder `config`.\n\n\n## Development Guidelines\nIf you are developing I still recommend using docker. with -v flags in docker you could mount your\npc's folders on the docker. I recommend keeping all the packages in folder `docker_ws_slam/catkin_ws/src`\non your host pc. And all the rosbags in folder `/media/mpkuse/Bulk_Data`. And then mount these\ntwo folders on the docker-container. Edit the following command as needed.\n\n\n```\ndocker run --runtime=nvidia -it  -v /media/mpkuse/Bulk_Data/:/Bulk_Data  -v /home/mpkuse/docker_ws_slam:/app  --add-host `hostname`:172.17.0.1  --env ROS_MASTER_URI=http://`hostname`:11311/  --env CUDA_VISIBLE_DEVICES=0  --hostname happy_go   --name happy_go  mpkuse/kusevisionkit:ros-kinetic-vins bash\n```\n\n\nUse the following if you wish to use xhost(gui) from inside docker\n```\n$(host) xhost +local:root\n$(host)\ndocker run --runtime=nvidia -it  -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix -v /media/mpkuse/Bulk_Data/:/Bulk_Data  -v /home/mpkuse/docker_ws_slam:/app  --add-host `hostname`:172.17.0.1  --env ROS_MASTER_URI=http://`hostname`:11311/  --env CUDA_VISIBLE_DEVICES=0  --hostname happy_go   --name happy_go  mpkuse/kusevisionkit:ros-kinetic-vins bash\n```\n\nOther docker images that work:\n- mpkuse/kusevisionkit:ros-kinetic-vins-tf-faiss (preferred)\n- mpkuse/kusevisionkit:vins-kidnap\n\nEach of my classes can export the data they hold as json objects and image files. Look at the\nend of `main()` in `cerebro_node.cpp` and modify as needed to extract more debug data. Similarly\nthe pose graph solver can also be debugged.\nIf writing a lot of debug data (usually taking more than 10sec) roslaunch force-kills the process.\nTo get around this issue, you need to increase the timeout:\n\n```\nFor kinetic this can be found in:\n/opt/ros/kinetic/lib/python2.7/dist-packages/roslaunch\nIn the file nodeprocess.py there will be 2 variables on line 57 and 58.\n\n_TIMEOUT_SIGINT = 15.0 #seconds\n\n_TIMEOUT_SIGTERM = 2.0 #seconds\n```\n\n\nFor streamlining printing messages\nI have preprocessor macros at the start of function implementation (of classes DataManager and Cerebro),\nread the comments there and edit as per need. Try to implement your algorithms in\nobject oriented way and using the producer-consumer paradigm. Look at my thread-mains for example.  \n\nFinally, sensible PR with bug fixes, enhancements are welcome!\n![](doc/rosgraph.png)\n\n## Authors\nManohar Kuse <mpkuse@connect.ust.hk>\n"
  },
  {
    "path": "config/echo__terminator_config_vins_fusion_cerebro",
    "content": "[global_config]\n[keybindings]\n[layouts]\n  [[default]]\n    [[[child0]]]\n      fullscreen = False\n      last_active_term = 1cc3df64-f997-4766-9317-0ce75d5dc2cd\n      last_active_window = True\n      maximised = True\n      order = 0\n      parent = \"\"\n      position = 65:24\n      size = 1855, 1056\n      title = dji@MANIFOLD-2-C: ~\n      type = Window\n    [[[child1]]]\n      order = 0\n      parent = child0\n      position = 525\n      ratio = 0.5\n      type = VPaned\n    [[[child2]]]\n      order = 0\n      parent = child1\n      position = 513\n      ratio = 0.278167115903\n      type = HPaned\n    [[[child4]]]\n      order = 1\n      parent = child2\n      position = 648\n      ratio = 0.487275449102\n      type = HPaned\n    [[[child7]]]\n      order = 1\n      parent = child1\n      position = 513\n      ratio = 0.278167115903\n      type = HPaned\n    [[[child9]]]\n      order = 1\n      parent = child7\n      position = 644\n      ratio = 0.484281437126\n      type = HPaned\n    [[[terminal10]]]\n      command = echo \"roslaunch cerebro realsense_vinsfusion_ondrone.launch\"; echo \"roslaunch cerebro mynteye_vinsfusion_ondrone.launch\"; bash\n      order = 0\n      parent = child9\n      profile = default\n      type = Terminal\n      uuid = 8fc7a21c-3feb-468d-aae5-c363bc5c9f80\n    [[[terminal11]]]\n      command = echo \"roslaunch cerebro realsense_camera.launch\"; echo \"roslaunch mynt_eye_ros_wrapper mynteye.launch\"; echo \"rosbag play <file.bag>\"; bash\n      order = 1\n      parent = child9\n      profile = default\n      type = Terminal\n      uuid = a63545ab-f3c1-4426-a8ef-173f57ca8f28\n    [[[terminal3]]]\n      command = echo \"roscore\"; bash\n      order = 0\n      parent = child2\n      profile = default\n      type = Terminal\n      uuid = 27ae967a-b56c-4d92-a56e-67a2abe6b22d\n    [[[terminal5]]]\n      command = '''echo \"ssh root@dji-tx2 'nvpmodel -m 0 && jetson_clocks'\"; echo \"\"; echo \"ssh -t dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash && roslaunch tx2_whole_image_desc_server py3_tf_server_realsense.launch'\" ; echo \"ssh -t dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash && roslaunch tx2_whole_image_desc_server py3_tf_server_mynteye.launch'\"; bash'''\n      order = 0\n      parent = child4\n      profile = default\n      type = Terminal\n      uuid = 6184900a-1468-4ffa-96a0-b1c00563ead7\n    [[[terminal6]]]\n      command = '''echo \"ssh dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash &&  rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam' \"; bash'''\n      order = 1\n      parent = child4\n      profile = default\n      type = Terminal\n      uuid = 09e32ec9-5e11-4bb0-980d-4c1abeb77791\n    [[[terminal8]]]\n      command = echo \"rviz -d catkin_ws/src/cerebro/rviz/vins-fusion.rviz\"; bash\n      order = 0\n      parent = child7\n      profile = default\n      type = Terminal\n      uuid = 1cc3df64-f997-4766-9317-0ce75d5dc2cd\n  [[vins-cerebro]]\n    [[[child0]]]\n      fullscreen = False\n      last_active_term = b015529f-251d-4563-81f8-86f2d24badbe\n      last_active_window = True\n      maximised = False\n      order = 0\n      parent = \"\"\n      position = 289:106\n      size = 642, 378\n      title = dji@MANIFOLD-2-C: ~\n      type = Window\n    [[[terminal1]]]\n      order = 0\n      parent = child0\n      profile = vins-cerebro\n      type = Terminal\n      uuid = b015529f-251d-4563-81f8-86f2d24badbe\n[plugins]\n[profiles]\n  [[default]]\n    background_image = None\n    scrollback_lines = 5000\n  [[vins-cerebro]]\n    background_image = None\n"
  },
  {
    "path": "config/euroc/euroc_config_no_extrinsic.yaml",
    "content": "%YAML:1.0\n\n#common parameters\nimu_topic: \"/imu0\"\nimage_topic: \"/cam0/image_raw\"\noutput_path: \"/home/tony-ws1/output/\"\n\n#camera calibration \nmodel_type: PINHOLE\ncamera_name: camera\nimage_width: 752\nimage_height: 480\ndistortion_parameters:\n   k1: -2.917e-01\n   k2: 8.228e-02\n   p1: 5.333e-05\n   p2: -1.578e-04\nprojection_parameters:\n   fx: 4.616e+02\n   fy: 4.603e+02\n   cx: 3.630e+02\n   cy: 2.481e+02\n\n# Extrinsic parameter between IMU and Camera.\nestimate_extrinsic: 2   # 0  Have an accurate extrinsic parameters. We will trust the following imu^R_cam, imu^T_cam, don't change it.\n                        # 1  Have an initial guess about extrinsic parameters. We will optimize around your initial guess.\n                        # 2  Don't know anything about extrinsic parameters. You don't need to give R,T. We will try to calibrate it. Do some rotation movement at beginning.                        \n#If you choose 0 or 1, you should write down the following matrix.\n\n#feature traker paprameters\nmax_cnt: 150            # max feature number in feature tracking\nmin_dist: 30            # min distance between two features \nfreq: 10                # frequence (Hz) of publish tracking result. At least 10Hz for good estimation. If set 0, the frequence will be same as raw image \nF_threshold: 1.0        # ransac threshold (pixel)\nshow_track: 1           # publish tracking image as topic\nequalize: 1             # if image is too dark or light, trun on equalize to find enough features\nfisheye: 0              # if using fisheye, trun on it. A circle mask will be loaded to remove edge noisy points\n\n#optimization parameters\nmax_solver_time: 0.04  # max solver itration time (ms), to guarantee real time\nmax_num_iterations: 8   # max solver itrations, to guarantee real time\nkeyframe_parallax: 10.0 # keyframe selection threshold (pixel)\n\n#imu parameters       The more accurate parameters you provide, the better performance\nacc_n: 0.2          # accelerometer measurement noise standard deviation. #0.2\ngyr_n: 0.02         # gyroscope measurement noise standard deviation.     #0.05\nacc_w: 0.0002         # accelerometer bias random work noise standard deviation.  #0.02\ngyr_w: 2.0e-5       # gyroscope bias random work noise standard deviation.     #4.0e-5\ng_norm: 9.81007     # gravity magnitude\n\n#loop closure parameters\nloop_closure: 1                 # start loop closure\nload_previous_pose_graph: 0     # load and reuse previous pose graph; load from 'pose_graph_save_path'\nfast_relocalization: 0          # useful in real-time and large project\npose_graph_save_path: \"/home/tony-ws1/output/pose_graph/\" # save and load path\n\n#unsynchronization parameters\nestimate_td: 0                      # online estimate time offset between camera and imu\ntd: 0.0                             # initial value of time offset. unit: s. readed image clock + td = real image clock (IMU clock)\n\n#rolling shutter parameters\nrolling_shutter: 0                  # 0: global shutter camera, 1: rolling shutter camera\nrolling_shutter_tr: 0               # unit: s. rolling shutter read out time per frame (from data sheet). \n\n#visualization parameters\nsave_image: 1                   # save image in pose graph for visualization prupose; you can close this function by setting 0 \nvisualize_imu_forward: 0        # output imu forward propogation to achieve low latency and high frequence results\nvisualize_camera_size: 0.4      # size of camera marker in RVIZ"
  },
  {
    "path": "config/fly",
    "content": "[global_config]\n[keybindings]\n[layouts]\n  [[default]]\n    [[[child0]]]\n      fullscreen = False\n      last_active_term = 1cc3df64-f997-4766-9317-0ce75d5dc2cd\n      last_active_window = True\n      maximised = True\n      order = 0\n      parent = \"\"\n      position = 65:24\n      size = 1855, 1056\n      title = dji@MANIFOLD-2-C: ~\n      type = Window\n    [[[child1]]]\n      order = 0\n      parent = child0\n      position = 525\n      ratio = 0.5\n      type = VPaned\n    [[[child2]]]\n      order = 0\n      parent = child1\n      position = 513\n      ratio = 0.278167115903\n      type = HPaned\n    [[[child4]]]\n      order = 1\n      parent = child2\n      position = 648\n      ratio = 0.487275449102\n      type = HPaned\n    [[[child7]]]\n      order = 1\n      parent = child1\n      position = 513\n      ratio = 0.278167115903\n      type = HPaned\n    [[[child9]]]\n      order = 1\n      parent = child7\n      position = 644\n      ratio = 0.484281437126\n      type = HPaned\n    [[[terminal10]]]\n      command = echo \"roslaunch cerebro realsense_vinsfusion.launch\"; sleep 15s; roslaunch --wait cerebro realsense_vinsfusion.launch; bash\n      order = 0\n      parent = child9\n      profile = default\n      type = Terminal\n      uuid = 8fc7a21c-3feb-468d-aae5-c363bc5c9f80\n    [[[terminal11]]]\n      command = echo \"roslaunch cerebro realsense_camera.launch\"; sleep 5s; roslaunch --wait cerebro realsense_camera.launch;  bash\n      order = 1\n      parent = child9\n      profile = default\n      type = Terminal\n      uuid = a63545ab-f3c1-4426-a8ef-173f57ca8f28\n    [[[terminal3]]]\n      command = echo \"roscore\"; roscore; bash\n      order = 0\n      parent = child2\n      profile = default\n      type = Terminal\n      uuid = 27ae967a-b56c-4d92-a56e-67a2abe6b22d\n    [[[terminal5]]]\n      #command = '''echo \"ssh root@dji-tx2 'nvpmodel -m 0 && jetson_clocks'\"; echo \"\"; echo \"ssh -t dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash && roslaunch tx2_whole_image_desc_server py3_tf_server_realsense.launch'\" ; echo \"ssh -t dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash && roslaunch tx2_whole_image_desc_server py3_tf_server_mynteye.launch'\"; bash'''\n      command = ''' coproc rviz -d catkin_ws/src/cerebro/rviz/vins-fusion.rviz ;  ssh root@dji-tx2 'nvpmodel -m 0 && jetson_clocks'; ssh -t dji@dji-tx2 \"export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; export DJIROS_APPID='1014582' ; export DJIROS_ENCKEY='821c8c3abeb6e340637b01f908ddeefb6c76d18d5f289e5cdb3cd5756a17bef5'; source $HOME/catkin_ws/devel/setup.bash && roslaunch djiros djiros.launch\" ; bash '''\n      order = 0\n      parent = child4\n      profile = default\n      type = Terminal\n      uuid = 6184900a-1468-4ffa-96a0-b1c00563ead7\n    [[[terminal6]]]\n      #command = '''echo \"ssh dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash &&  rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam' \"; bash'''\n      command = \" sleep 3s; echo 'rosrun trajectory_node trajectory_node' ; export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; rosrun trajectory_node trajectory_node ; bash \"\n      order = 1\n      parent = child4\n      profile = default\n      type = Terminal\n      uuid = 09e32ec9-5e11-4bb0-980d-4c1abeb77791\n    [[[terminal8]]]\n      command = echo \"roslaunch --wait machine_defined ctrl_md.launch\"; roslaunch --wait machine_defined ctrl_md.launch ; bash\n      order = 0\n      parent = child7\n      profile = default\n      type = Terminal\n      uuid = 1cc3df64-f997-4766-9317-0ce75d5dc2cd\n  [[vins-cerebro]]\n    [[[child0]]]\n      fullscreen = False\n      last_active_term = b015529f-251d-4563-81f8-86f2d24badbe\n      last_active_window = True\n      maximised = False\n      order = 0\n      parent = \"\"\n      position = 289:106\n      size = 642, 378\n      title = dji@MANIFOLD-2-C: ~\n      type = Window\n    [[[terminal1]]]\n      order = 0\n      parent = child0\n      profile = vins-cerebro\n      type = Terminal\n      uuid = b015529f-251d-4563-81f8-86f2d24badbe\n[plugins]\n[profiles]\n  [[default]]\n    background_image = None\n    scrollback_lines = 5000\n  [[vins-cerebro]]\n    background_image = None\n"
  },
  {
    "path": "config/mynteye/camera_left.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: MEI\ncamera_name: camera_left\nimage_width: 752\nimage_height: 480\nmirror_parameters:\n   xi: 9.1080215802208964e-01\ndistortion_parameters:\n   k1: -4.1813210333143819e-01\n   k2: 1.7403431210939191e-01\n   p1: 9.9413203823357906e-04\n   p2: 7.3177696678596699e-04\nprojection_parameters:\n   gamma1: 7.0261362770226128e+02\n   gamma2: 7.0163099053315807e+02\n   u0: 3.7079141993914720e+02\n   v0: 2.3833755987148535e+02\n"
  },
  {
    "path": "config/mynteye/camera_right.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: MEI\ncamera_name: camera_right\nimage_width: 752\nimage_height: 480\nmirror_parameters:\n   xi: 1.0430682060403200e+00\ndistortion_parameters:\n   k1: -4.1052639675713765e-01\n   k2: 1.5594870262700564e-01\n   p1: 1.0690635619397511e-03\n   p2: -1.2541037785533519e-04\nprojection_parameters:\n   gamma1: 7.5079430544256968e+02\n   gamma2: 7.5002280082558298e+02\n   u0: 3.6187005259990548e+02\n   v0: 2.4784480598161096e+02\n"
  },
  {
    "path": "config/mynteye/extrinsics.yaml",
    "content": "%YAML:1.0\n---\ntransform:\n   q_x: -1.8252509868889259e-04\n   q_y: -1.6291774489779708e-03\n   q_z: -1.2462127842978489e-03\n   q_w: 9.9999787970731446e-01\n   t_x: -1.2075905420832895e+02\n   t_y: 5.4110610639412482e-01\n   t_z: 2.4484815673909591e-01\n"
  },
  {
    "path": "config/mynteye/mynteye_config.yaml",
    "content": "%YAML:1.0\n\n#common parameters\nimu_topic: \"/mynteye/imu/data_raw\"\nimage_topic: \"/mynteye/left/image_raw\"\noutput_path: \"/Bulk_Data/vins-mono-output/\"\n\n# Additional camera (stereo) for cerebro - added my mpkuse\nimage_topic_1: \"/mynteye/right/image_raw\"\ncamera_yaml_1: \"camera_right.yaml\" # this yaml file will be searched in directory where this yaml file is.\nextrinsic_1_T_0: \"extrinsics.yaml\" # TODO contains the right_T_left, ie. stereo baseline. I need right_T_left that a transform that transforms 3d points of left to 3d points of right. in other words, if you sir of right the position of left camera.\n\n#camera calibration\nmodel_type: MEI\ncamera_name: camera_left\nimage_width: 752\nimage_height: 480\nmirror_parameters:\n   xi: 9.1080215802208964e-01\ndistortion_parameters:\n   k1: -4.1813210333143819e-01\n   k2: 1.7403431210939191e-01\n   p1: 9.9413203823357906e-04\n   p2: 7.3177696678596699e-04\nprojection_parameters:\n   gamma1: 7.0261362770226128e+02\n   gamma2: 7.0163099053315807e+02\n   u0: 3.7079141993914720e+02\n   v0: 2.3833755987148535e+02\n\n# Extrinsic parameter between IMU and Camera.\nestimate_extrinsic: 1   # 0  Have an accurate extrinsic parameters. We will trust the following imu^R_cam, imu^T_cam, don't change it.\n                        # 1  Have an initial guess about extrinsic parameters. We will optimize around your initial guess.\n                        # 2  Don't know anything about extrinsic parameters. You don't need to give R,T. We will try to calibrate it. Do some rotation movement at beginning.\n#If you choose 0 or 1, you should write down the following matrix.\n#Rotation from camera frame to imu frame, imu^R_cam\nextrinsicRotation: !!opencv-matrix\n   rows: 3\n   cols: 3\n   dt: d\n   data: [ 4.2739206953025244e-03, -9.9997227378122833e-01,\n       -6.0979726705474944e-03, 9.9998919890819160e-01,\n       4.2626966744552242e-03, 1.8524265207998580e-03,\n       -1.8263813521932205e-03, -6.1058239298286089e-03,\n       9.9997969141642784e-01 ]\nextrinsicTranslation: !!opencv-matrix\n   rows: 3\n   cols: 1\n   dt: d\n   data: [ 6.1040452082857912e-03, -4.8408978130397302e-02,\n       2.2594515206886469e-02 ]\n\n#feature traker paprameters\nmax_cnt: 150            # max feature number in feature tracking\nmin_dist: 25            # min distance between two features\nfreq: 10                # frequence (Hz) of publish tracking result. At least 10Hz for good estimation. If set 0, the frequence will be same as raw image\nF_threshold: 1.0        # ransac threshold (pixel)\nshow_track: 1           # publish tracking image as topic\nequalize: 1             # if image is too dark or light, trun on equalize to find enough features\nfisheye: 0              # if using fisheye, trun on it. A circle mask will be loaded to remove edge noisy points\n\n#optimization parameters\nmax_solver_time: 0.04  # max solver itration time (ms), to guarantee real time\nmax_num_iterations: 8   # max solver itrations, to guarantee real time\nkeyframe_parallax: 10.0 # keyframe selection threshold (pixel)\n\n#imu parameters       The more accurate parameters you provide, the better performance\nacc_n: 0.04          # accelerometer measurement noise standard deviation. #0.2   0.04\ngyr_n: 0.004         # gyroscope measurement noise standard deviation.     #0.05  0.004\nacc_w: 0.0004         # accelerometer bias random work noise standard deviation.  #0.02\ngyr_w: 2.0e-5       # gyroscope bias random work noise standard deviation.     #4.0e-5\ng_norm: 9.80766     # gravity magnitude\n\n#loop closure parameters\nloop_closure: 0                    # start loop closure\nload_previous_pose_graph: 0        # load and reuse previous pose graph; load from 'pose_graph_save_path'\nfast_relocalization: 0             # useful in real-time and large project\npose_graph_save_path: \"/home/tony-ws1/output/pose_graph/\" # save and load path\n\n#unsynchronization parameters\nestimate_td: 0                      # online estimate time offset between camera and imu\ntd: 0.0                             # initial value of time offset. unit: s. readed image clock + td = real image clock (IMU clock)\n\n#rolling shutter parameters\nrolling_shutter: 0                  # 0: global shutter camera, 1: rolling shutter camera\nrolling_shutter_tr: 0               # unit: s. rolling shutter read out time per frame (from data sheet).\n\n#visualization parameters\nsave_image: 0                   # save image in pose graph for visualization prupose; you can close this function by setting 0\nvisualize_imu_forward: 0        # output imu forward propogation to achieve low latency and high frequence results\nvisualize_camera_size: 0.4      # size of camera marker in RVIZ\n"
  },
  {
    "path": "config/mynteye__terminator_config_vins_fusion_cerebro",
    "content": "[global_config]\n[keybindings]\n[layouts]\n  [[default]]\n    [[[child0]]]\n      fullscreen = False\n      last_active_term = 1cc3df64-f997-4766-9317-0ce75d5dc2cd\n      last_active_window = True\n      maximised = True\n      order = 0\n      parent = \"\"\n      position = 65:24\n      size = 1855, 1056\n      title = dji@MANIFOLD-2-C: ~\n      type = Window\n    [[[child1]]]\n      order = 0\n      parent = child0\n      position = 525\n      ratio = 0.5\n      type = VPaned\n    [[[child2]]]\n      order = 0\n      parent = child1\n      position = 513\n      ratio = 0.278167115903\n      type = HPaned\n    [[[child4]]]\n      order = 1\n      parent = child2\n      position = 648\n      ratio = 0.487275449102\n      type = HPaned\n    [[[child7]]]\n      order = 1\n      parent = child1\n      position = 513\n      ratio = 0.278167115903\n      type = HPaned\n    [[[child9]]]\n      order = 1\n      parent = child7\n      position = 644\n      ratio = 0.484281437126\n      type = HPaned\n    [[[terminal10]]]\n      command = '''echo \"roslaunch cerebro realsense_vinsfusion_ondrone.launch\";\n\t\techo \"roslaunch cerebro mynteye_vinsfusion_ondrone.launch\";\n        export KUSE_CMD=\"roslaunch cerebro mynteye_vinsfusion_ondrone.launch\";\n\t\troslaunch --wait cerebro mynteye_vinsfusion_ondrone.launch;\n\t\tbash'''\n      order = 0\n      parent = child9\n      profile = default\n      type = Terminal\n      uuid = 8fc7a21c-3feb-468d-aae5-c363bc5c9f80\n    [[[terminal11]]]\n      command = '''#echo \"roslaunch cerebro realsense_camera.launch\";\n\t\techo \"roslaunch mynt_eye_ros_wrapper mynteye.launch\";\n\t\techo \"rosbag play <file.bag>\";\n        export KUSE_CMD=\"roslaunch mynt_eye_ros_wrapper mynteye.launch or rosbag play\"\n        bash'''\n      order = 1\n      parent = child9\n      profile = default\n      type = Terminal\n      uuid = a63545ab-f3c1-4426-a8ef-173f57ca8f28\n    [[[terminal3]]]\n      command = echo \"roscore\"; exec \"roscore\"; bash\n      order = 0\n      parent = child2\n      profile = default\n      type = Terminal\n      uuid = 27ae967a-b56c-4d92-a56e-67a2abe6b22d\n    [[[terminal5]]]\n      command = '''echo \"ssh root@dji-tx2 'nvpmodel -m 0 && jetson_clocks'\";\n\t\t\tssh root@dji-tx2 'nvpmodel -m 0 && jetson_clocks';\n\t\t\techo \"\";\n\t\t\t#echo \"ssh -t dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash && roslaunch --wait tx2_whole_image_desc_server py3_tf_server_realsense.launch'\";\n\t\t\techo \"ssh -t dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash && roslaunch --wait tx2_whole_image_desc_server py3_tf_server_mynteye.launch'\";\n            export KUSE_CMD=\"ssh -t dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash && roslaunch --wait tx2_whole_image_desc_server py3_tf_server_mynteye.launch'\";\n\t\t\tssh -t dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash && roslaunch --wait tx2_whole_image_desc_server py3_tf_server_mynteye.launch'\n\t\t\tbash'''\n      order = 0\n      parent = child4\n      profile = default\n      type = Terminal\n      uuid = 6184900a-1468-4ffa-96a0-b1c00563ead7\n    [[[terminal6]]]\n      command = '''echo \"ssh dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash &&  rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam'\";\n        \texport KUSE_CMD=\"ssh dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash &&  rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam'\";\n\t\tssh dji@dji-tx2 \"export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash &&  rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam\";\n\t\tbash'''\n      order = 1\n      parent = child4\n      profile = default\n      type = Terminal\n      uuid = 09e32ec9-5e11-4bb0-980d-4c1abeb77791\n    [[[terminal8]]]\n      command = '''echo \"rviz -d catkin_ws/src/cerebro/rviz/vins-fusion.rviz\";\n\t\trviz -d catkin_ws/src/cerebro/rviz/vins-fusion.rviz\n\t\tbash'''\n      order = 0\n      parent = child7\n      profile = default\n      type = Terminal\n      uuid = 1cc3df64-f997-4766-9317-0ce75d5dc2cd\n  [[vins-cerebro]]\n    [[[child0]]]\n      fullscreen = False\n      last_active_term = b015529f-251d-4563-81f8-86f2d24badbe\n      last_active_window = True\n      maximised = False\n      order = 0\n      parent = \"\"\n      position = 289:106\n      size = 642, 378\n      title = dji@MANIFOLD-2-C: ~\n      type = Window\n    [[[terminal1]]]\n      order = 0\n      parent = child0\n      profile = vins-cerebro\n      type = Terminal\n      uuid = b015529f-251d-4563-81f8-86f2d24badbe\n[plugins]\n[profiles]\n  [[default]]\n    background_image = None\n    scrollback_lines = 5000\n  [[vins-cerebro]]\n    background_image = None\n"
  },
  {
    "path": "config/mynteye_kannala_brandt/camera_left.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: KANNALA_BRANDT\ncamera_name: camera_left\nimage_width: 752\nimage_height: 480\nprojection_parameters:\n   k2: -1.9372136316513616e-02\n   k3: -9.8140934466631590e-03\n   k4: 5.2090950264238800e-03\n   k5: -2.5384805224148016e-03\n   mu: 3.6779833417894611e+02\n   mv: 3.6735717027889308e+02\n   u0: 3.7150938938370604e+02\n   v0: 2.3994369620747401e+02\n"
  },
  {
    "path": "config/mynteye_kannala_brandt/camera_right.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: KANNALA_BRANDT\ncamera_name: camera_right\nimage_width: 752\nimage_height: 480\nprojection_parameters:\n   k2: -1.6877708507279571e-02\n   k3: -2.2125250367976849e-02\n   k4: 2.5629804328110812e-02\n   k5: -1.3352680211095494e-02\n   mu: 3.6746513955323167e+02\n   mv: 3.6703089274525593e+02\n   u0: 3.6200946050656484e+02\n   v0: 2.4919721516867821e+02\n"
  },
  {
    "path": "config/mynteye_kannala_brandt/extrinsics.yaml",
    "content": "%YAML:1.0\n---\ntransform:\n   q_x: -7.0955103716253032e-04\n   q_y: -1.5775578725333590e-03\n   q_z: -1.2732644461763854e-03\n   q_w: 9.9999769332040711e-01\n   t_x: -1.2006984141573309e+02\n   t_y: 3.3956264524978619e-01\n   t_z: -1.6784055634087214e-01\n"
  },
  {
    "path": "config/mynteye_kannala_brandt/mynteye_config.yaml",
    "content": "%YAML:1.0\n\n#common parameters\nimu_topic: \"/mynteye/imu/data_raw\"\nimage_topic: \"/mynteye/left/image_raw\"\noutput_path: \"/Bulk_Data/vins-mono-output/\"\n\n# Additional camera (stereo) for cerebro - added my mpkuse\nimage_topic_1: \"/mynteye/right/image_raw\"\ncamera_yaml_1: \"camera_right.yaml\" # this yaml file will be searched in directory where this yaml file is.\nextrinsic_1_T_0: \"extrinsics.yaml\" #  contains the right_T_left, ie. stereo baseline. I need right_T_left that a transform that transforms 3d points of left to 3d points of right. in other words, if you sir of right the position of left camera.\n\n# publish image pair on loopcandidate detection\n\n\n#camera calibration\nmodel_type: KANNALA_BRANDT\ncamera_name: camera_left\nimage_width: 752\nimage_height: 480\nprojection_parameters:\n   k2: -1.9372136316513616e-02\n   k3: -9.8140934466631590e-03\n   k4: 5.2090950264238800e-03\n   k5: -2.5384805224148016e-03\n   mu: 3.6779833417894611e+02\n   mv: 3.6735717027889308e+02\n   u0: 3.7150938938370604e+02\n   v0: 2.3994369620747401e+02\n\n\n\n# Extrinsic parameter between IMU and Camera.\nestimate_extrinsic: 1   # 0  Have an accurate extrinsic parameters. We will trust the following imu^R_cam, imu^T_cam, don't change it.\n                        # 1  Have an initial guess about extrinsic parameters. We will optimize around your initial guess.\n                        # 2  Don't know anything about extrinsic parameters. You don't need to give R,T. We will try to calibrate it. Do some rotation movement at beginning.\n#If you choose 0 or 1, you should write down the following matrix.\n#Rotation from camera frame to imu frame, imu^R_cam\nextrinsicRotation: !!opencv-matrix\n   rows: 3\n   cols: 3\n   dt: d\n   data: [ 4.2739206953025244e-03, -9.9997227378122833e-01,\n       -6.0979726705474944e-03, 9.9998919890819160e-01,\n       4.2626966744552242e-03, 1.8524265207998580e-03,\n       -1.8263813521932205e-03, -6.1058239298286089e-03,\n       9.9997969141642784e-01 ]\nextrinsicTranslation: !!opencv-matrix\n   rows: 3\n   cols: 1\n   dt: d\n   data: [ 6.1040452082857912e-03, -4.8408978130397302e-02,\n       2.2594515206886469e-02 ]\n\n#feature traker paprameters\nmax_cnt: 150            # max feature number in feature tracking\nmin_dist: 25            # min distance between two features\nfreq: 10                # frequence (Hz) of publish tracking result. At least 10Hz for good estimation. If set 0, the frequence will be same as raw image\nF_threshold: 1.0        # ransac threshold (pixel)\nshow_track: 1           # publish tracking image as topic\nequalize: 1             # if image is too dark or light, trun on equalize to find enough features\nfisheye: 0              # if using fisheye, trun on it. A circle mask will be loaded to remove edge noisy points\n\n#optimization parameters\nmax_solver_time: 0.04  # max solver itration time (ms), to guarantee real time\nmax_num_iterations: 10   # max solver itrations, to guarantee real time\nkeyframe_parallax: 10.0 # keyframe selection threshold (pixel)\n\n#imu parameters       The more accurate parameters you provide, the better performance\nacc_n: 0.04          # accelerometer measurement noise standard deviation. #0.2   0.04\ngyr_n: 0.004         # gyroscope measurement noise standard deviation.     #0.05  0.004\nacc_w: 0.0004         # accelerometer bias random work noise standard deviation.  #0.02\ngyr_w: 2.0e-5       # gyroscope bias random work noise standard deviation.     #4.0e-5\ng_norm: 9.80766     # gravity magnitude\n\n#loop closure parameters\nloop_closure: 1                    # start loop closure\nload_previous_pose_graph: 0        # load and reuse previous pose graph; load from 'pose_graph_save_path'\nfast_relocalization: 1             # useful in real-time and large project\npose_graph_save_path: \"/Bulk_Data/vins-mono-output/pose_graph/\" # save and load path\n\n#unsynchronization parameters\nestimate_td: 0                      # online estimate time offset between camera and imu\ntd: 0.0                             # initial value of time offset. unit: s. readed image clock + td = real image clock (IMU clock)\n\n#rolling shutter parameters\nrolling_shutter: 0                  # 0: global shutter camera, 1: rolling shutter camera\nrolling_shutter_tr: 0               # unit: s. rolling shutter read out time per frame (from data sheet).\n\n#visualization parameters\nsave_image: 1                   # save image in pose graph for visualization prupose; you can close this function by setting 0\nvisualize_imu_forward: 0        # output imu forward propogation to achieve low latency and high frequence results\nvisualize_camera_size: 0.4      # size of camera marker in RVIZ\n"
  },
  {
    "path": "config/mynteye_pinhole/camera_left.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: PINHOLE\ncamera_name: camera_left\nimage_width: 752\nimage_height: 480\ndistortion_parameters:\n   k1: -3.0304011877067349e-01\n   k2: 7.8611376314970768e-02\n   p1: 1.3200427858719645e-03\n   p2: -1.2942361409192899e-03\nprojection_parameters:\n   fx: 3.7560167589977470e+02\n   fy: 3.7552589588133230e+02\n   cx: 3.7887177487131697e+02\n   cy: 2.3402525474822104e+02\n"
  },
  {
    "path": "config/mynteye_pinhole/camera_right.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: PINHOLE\ncamera_name: camera_right\nimage_width: 752\nimage_height: 480\ndistortion_parameters:\n   k1: -3.0790744874410708e-01\n   k2: 8.3037204977051693e-02\n   p1: 1.1347651021695062e-03\n   p2: -7.8671202943393290e-04\nprojection_parameters:\n   fx: 3.7573448135250288e+02\n   fy: 3.7536464618331070e+02\n   cx: 3.6634215933104940e+02\n   cy: 2.4325470902936044e+02\n"
  },
  {
    "path": "config/mynteye_pinhole/extrinsics.yaml",
    "content": "%YAML:1.0\n---\ntransform:\n   q_x: -7.8278125678242136e-04\n   q_y: 2.4666921736014214e-03\n   q_z: -1.4477856356187120e-03\n   q_w: 9.9999560329032322e-01\n   t_x: -1.1985630585227842e+02\n   t_y: 1.2186198275182303e-01\n   t_z: -1.4431533628123798e+00\n"
  },
  {
    "path": "config/mynteye_pinhole/mynteye_config.yaml",
    "content": "%YAML:1.0\n\n#common parameters\nimu_topic: \"/mynteye/imu/data_raw\"\nimage_topic: \"/mynteye/left/image_raw\"\noutput_path: \"/Bulk_Data/vins-mono-output/\"\n\n# Additional camera (stereo) for cerebro - added my mpkuse\nimage_topic_1: \"/mynteye/right/image_raw\"\ncamera_yaml_1: \"camera_right.yaml\" # this yaml file will be searched in directory where this yaml file is.\nextrinsic_1_T_0: \"extrinsics.yaml\" #  contains the right_T_left, ie. stereo baseline. I need right_T_left that a transform that transforms 3d points of left to 3d points of right. in other words, if you sir of right the position of left camera.\n\n#camera calibration\nmodel_type: PINHOLE\ncamera_name: camera_left\nimage_width: 752\nimage_height: 480\ndistortion_parameters:\n   k1: -3.0304011877067349e-01\n   k2: 7.8611376314970768e-02\n   p1: 1.3200427858719645e-03\n   p2: -1.2942361409192899e-03\nprojection_parameters:\n   fx: 3.7560167589977470e+02\n   fy: 3.7552589588133230e+02\n   cx: 3.7887177487131697e+02\n   cy: 2.3402525474822104e+02\n\n\n# Extrinsic parameter between IMU and Camera.\nestimate_extrinsic: 1   # 0  Have an accurate extrinsic parameters. We will trust the following imu^R_cam, imu^T_cam, don't change it.\n                        # 1  Have an initial guess about extrinsic parameters. We will optimize around your initial guess.\n                        # 2  Don't know anything about extrinsic parameters. You don't need to give R,T. We will try to calibrate it. Do some rotation movement at beginning.\n#If you choose 0 or 1, you should write down the following matrix.\n#Rotation from camera frame to imu frame, imu^R_cam\nextrinsicRotation: !!opencv-matrix\n   rows: 3\n   cols: 3\n   dt: d\n   data: [ 4.2739206953025244e-03, -9.9997227378122833e-01,\n       -6.0979726705474944e-03, 9.9998919890819160e-01,\n       4.2626966744552242e-03, 1.8524265207998580e-03,\n       -1.8263813521932205e-03, -6.1058239298286089e-03,\n       9.9997969141642784e-01 ]\nextrinsicTranslation: !!opencv-matrix\n   rows: 3\n   cols: 1\n   dt: d\n   data: [ 6.1040452082857912e-03, -4.8408978130397302e-02,\n       2.2594515206886469e-02 ]\n\n#feature traker paprameters\nmax_cnt: 130            # max feature number in feature tracking\nmin_dist: 25            # min distance between two features\nfreq: 10                # frequence (Hz) of publish tracking result. At least 10Hz for good estimation. If set 0, the frequence will be same as raw image\nF_threshold: 1.0        # ransac threshold (pixel)\nshow_track: 1           # publish tracking image as topic\nequalize: 1             # if image is too dark or light, trun on equalize to find enough features\nfisheye: 0              # if using fisheye, trun on it. A circle mask will be loaded to remove edge noisy points\n\n#optimization parameters\nmax_solver_time: 0.04  # max solver itration time (ms), to guarantee real time\nmax_num_iterations: 8   # max solver itrations, to guarantee real time\nkeyframe_parallax: 10.0 # keyframe selection threshold (pixel)\n\n#imu parameters       The more accurate parameters you provide, the better performance\nacc_n: 0.04          # accelerometer measurement noise standard deviation. #0.2   0.04\ngyr_n: 0.004         # gyroscope measurement noise standard deviation.     #0.05  0.004\nacc_w: 0.0004         # accelerometer bias random work noise standard deviation.  #0.02\ngyr_w: 2.0e-5       # gyroscope bias random work noise standard deviation.     #4.0e-5\ng_norm: 9.80766     # gravity magnitude\n\n#loop closure parameters\nloop_closure: 0                    # start loop closure\nload_previous_pose_graph: 0        # load and reuse previous pose graph; load from 'pose_graph_save_path'\nfast_relocalization: 0             # useful in real-time and large project\npose_graph_save_path: \"/home/tony-ws1/output/pose_graph/\" # save and load path\n\n#unsynchronization parameters\nestimate_td: 0                      # online estimate time offset between camera and imu\ntd: 0.0                             # initial value of time offset. unit: s. readed image clock + td = real image clock (IMU clock)\n\n#rolling shutter parameters\nrolling_shutter: 0                  # 0: global shutter camera, 1: rolling shutter camera\nrolling_shutter_tr: 0               # unit: s. rolling shutter read out time per frame (from data sheet).\n\n#visualization parameters\nsave_image: 0                   # save image in pose graph for visualization prupose; you can close this function by setting 0\nvisualize_imu_forward: 0        # output imu forward propogation to achieve low latency and high frequence results\nvisualize_camera_size: 0.4      # size of camera marker in RVIZ\n"
  },
  {
    "path": "config/realsense__terminator_config_vins_fusion_cerebro",
    "content": "[global_config]\n[keybindings]\n[layouts]\n  [[default]]\n    [[[child0]]]\n      fullscreen = False\n      last_active_term = 1cc3df64-f997-4766-9317-0ce75d5dc2cd\n      last_active_window = True\n      maximised = True\n      order = 0\n      parent = \"\"\n      position = 65:24\n      size = 1855, 1056\n      title = dji@MANIFOLD-2-C: ~\n      type = Window\n    [[[child1]]]\n      order = 0\n      parent = child0\n      position = 525\n      ratio = 0.5\n      type = VPaned\n    [[[child2]]]\n      order = 0\n      parent = child1\n      position = 513\n      ratio = 0.278167115903\n      type = HPaned\n    [[[child4]]]\n      order = 1\n      parent = child2\n      position = 648\n      ratio = 0.487275449102\n      type = HPaned\n    [[[child7]]]\n      order = 1\n      parent = child1\n      position = 513\n      ratio = 0.278167115903\n      type = HPaned\n    [[[child9]]]\n      order = 1\n      parent = child7\n      position = 644\n      ratio = 0.484281437126\n      type = HPaned\n    [[[terminal10]]]\n      command = '''echo \"roslaunch cerebro realsense_vinsfusion_ondrone.launch\";\n\t\troslaunch --wait cerebro realsense_vinsfusion_ondrone.launch\n\t\t#echo \"roslaunch cerebro mynteye_vinsfusion_ondrone.launch\";\n\t\t#roslaunch --wait cerebro mynteye_vinsfusion_ondrone.launch;\n\t\tbash'''\n      order = 0\n      parent = child9\n      profile = default\n      type = Terminal\n      uuid = 8fc7a21c-3feb-468d-aae5-c363bc5c9f80\n    [[[terminal11]]]\n      command = '''echo \"roslaunch cerebro realsense_camera.launch\";\n\t\t#echo \"roslaunch mynt_eye_ros_wrapper mynteye.launch\";\n\t\techo \"rosbag play <file.bag>\"; bash'''\n      order = 1\n      parent = child9\n      profile = default\n      type = Terminal\n      uuid = a63545ab-f3c1-4426-a8ef-173f57ca8f28\n    [[[terminal3]]]\n      command = echo \"roscore\"; exec \"roscore\"; bash\n      order = 0\n      parent = child2\n      profile = default\n      type = Terminal\n      uuid = 27ae967a-b56c-4d92-a56e-67a2abe6b22d\n    [[[terminal5]]]\n      command = '''echo \"ssh root@dji-tx2 'nvpmodel -m 0 && jetson_clocks'\";\n\t\t\tssh root@dji-tx2 'nvpmodel -m 0 && jetson_clocks';\n\t\t\techo \"\";\n\t\t\t#echo \"ssh -t dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash && roslaunch --wait tx2_whole_image_desc_server py3_tf_server_realsense.launch'\";\n\t\t\tssh -t dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash && roslaunch --wait tx2_whole_image_desc_server py3_tf_server_realsense.launch'\n\t\t\t#echo \"ssh -t dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash && roslaunch --wait tx2_whole_image_desc_server py3_tf_server_mynteye.launch'\";\n\t\t\t#ssh -t dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash && roslaunch --wait tx2_whole_image_desc_server py3_tf_server_mynteye.launch'\n\t\t\tbash'''\n      order = 0\n      parent = child4\n      profile = default\n      type = Terminal\n      uuid = 6184900a-1468-4ffa-96a0-b1c00563ead7\n    [[[terminal6]]]\n      command = '''echo \"ssh dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash &&  rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam'\";\n\t\tssh dji@dji-tx2 'export ROS_MASTER_URI=http://MANIFOLD-2-C:11311/; source $HOME/catkin_ws/devel/setup.bash &&  rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam';\n\t\tbash'''\n      order = 1\n      parent = child4\n      profile = default\n      type = Terminal\n      uuid = 09e32ec9-5e11-4bb0-980d-4c1abeb77791\n    [[[terminal8]]]\n      command = '''echo \"rviz -d catkin_ws/src/cerebro/rviz/vins-fusion.rviz\";\n\t\trviz -d catkin_ws/src/cerebro/rviz/vins-fusion.rviz\n\t\tbash'''\n      order = 0\n      parent = child7\n      profile = default\n      type = Terminal\n      uuid = 1cc3df64-f997-4766-9317-0ce75d5dc2cd\n  [[vins-cerebro]]\n    [[[child0]]]\n      fullscreen = False\n      last_active_term = b015529f-251d-4563-81f8-86f2d24badbe\n      last_active_window = True\n      maximised = False\n      order = 0\n      parent = \"\"\n      position = 289:106\n      size = 642, 378\n      title = dji@MANIFOLD-2-C: ~\n      type = Window\n    [[[terminal1]]]\n      order = 0\n      parent = child0\n      profile = vins-cerebro\n      type = Terminal\n      uuid = b015529f-251d-4563-81f8-86f2d24badbe\n[plugins]\n[profiles]\n  [[default]]\n    background_image = None\n    scrollback_lines = 5000\n  [[vins-cerebro]]\n    background_image = None\n"
  },
  {
    "path": "config/vinsfusion/djirosimu_realsense_d435i/README.md",
    "content": "The configuration when using realsense stereo camera and djiros's imu (ie. imu data from N3)\n"
  },
  {
    "path": "config/vinsfusion/djirosimu_realsense_d435i/extrinsics.yaml",
    "content": "%YAML:1.0\n---\ntransform:\n   q_x: 0.\n   q_y: 0.\n   q_z: 0.\n   q_w: 1.\n   t_x: -49.8\n   t_y: 0.\n   t_z: 0.\n#4.98 cm for the realsense d435i device. This device has hardware undistort so can safely use the pinhole model \n"
  },
  {
    "path": "config/vinsfusion/djirosimu_realsense_d435i/left.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: PINHOLE\ncamera_name: camera\nimage_width: 640\nimage_height: 480\ndistortion_parameters:\n   k1: 0.0\n   k2: 0.0\n   p1: 0.0\n   p2: 0.0\nprojection_parameters:\n   fx: 385.7544860839844\n   fy: 385.7544860839844\n   cx: 323.1204833984375\n   cy: 236.7432098388672\n"
  },
  {
    "path": "config/vinsfusion/djirosimu_realsense_d435i/realsense_stereo_imu_config.yaml",
    "content": "%YAML:1.0\n\n#common parameters\n#support: 1 imu 1 cam; 1 imu 2 cam: 2 cam;\nimu: 1\nnum_of_cam: 2\n\n# imu_topic: \"/camera/imu\"\nimu_topic: \"/djiros/imu\"\nimage0_topic: \"/camera/infra1/image_rect_raw\"\nimage1_topic: \"/camera/infra2/image_rect_raw\"\noutput_path: \"/home/dji/vinsfusion-output/\"\n\ncam0_calib: \"left.yaml\"\ncam1_calib: \"right.yaml\"\nimage_width: 640\nimage_height: 480\n\n\n\n# contains the right_T_left aka 1_T_0, ie. stereo baseline. I need right_T_left that a transform that transforms 3d points of left to 3d points of right. in other words, if you sure of right the position of left camera.\n# This is an optional argument even if using `num_of_cam:=2`. If I cannot\n# find this key I will use `body_T_cam0` and `body_T_cam1` to compute cam1_T_cam0.\n# But if this key exists, we will use these values as stereo baseline.\n# **In this file, I assume translation re specified ****in mm**** (and not in meters).**\nextrinsic_1_T_0: \"extrinsics.yaml\"\n\n\n# Extrinsic parameter between IMU and Camera.\nestimate_extrinsic: 1   # 0  Have an accurate extrinsic parameters. We will trust the following imu^R_cam, imu^T_cam, don't change it.\n                        # 1  Have an initial guess about extrinsic parameters. We will optimize around your initial guess.\n\n\nbody_T_cam0: !!opencv-matrix\n   rows: 4\n   cols: 4\n   dt: d\n   data: [ -1.0406548080980293e-02, -6.8339359975994052e-04,\n       9.9994561688635142e-01, 5.2600599061337287e-02,\n       -9.9994461366801901e-01, -1.5656625000493030e-03,\n       -1.0407607662295648e-02, 2.5510536758700533e-02,\n       1.5726898469127781e-03, -9.9999854083599726e-01,\n       -6.6706260700977182e-04, 9.5887981235633201e-03, 0., 0., 0., 1. ]\n\n\n\nbody_T_cam1: !!opencv-matrix\n   rows: 4\n   cols: 4\n   dt: d\n   data: [ -1.3185778504660295e-02, 1.2513252883544768e-04,\n       9.9991305601390978e-01, 4.2975722207919849e-02,\n       -9.9984703667423980e-01, 1.1490149582040754e-02,\n       -1.3186345829069746e-02, -5.4397185804457168e-02,\n       -1.1490800623434938e-02, -9.9993397822277874e-01,\n       -2.6393179226591457e-05, 1.8167602224138771e-02, 0., 0., 0., 1. ]\n\n\n\n#Multiple thread support\nmultiple_thread: 1\n\n#feature traker paprameters\nmax_cnt: 150            # max feature number in feature tracking\nmin_dist: 30            # min distance between two features\nfreq: 10                # frequence (Hz) of publish tracking result. At least 10Hz for good estimation. If set 0, the frequence will be same as raw image\nF_threshold: 1.0        # ransac threshold (pixel)\nshow_track: 0           # publish tracking image as topic\nflow_back: 1            # perform forward and backward optical flow to improve feature tracking accuracy\n\n#optimization parameters\nmax_solver_time: 0.04  # max solver itration time (ms), to guarantee real time\nmax_num_iterations: 8   # max solver itrations, to guarantee real time\nkeyframe_parallax: 10.0 # keyframe selection threshold (pixel)\n\n#imu parameters       The more accurate parameters you provide, the better performance\nacc_n: 0.1          # accelerometer measurement noise standard deviation. #0.2   0.04\ngyr_n: 0.02         # gyroscope measurement noise standard deviation.     #0.05  0.004\nacc_w: 0.002         # accelerometer bias random work noise standard deviation.  #0.002\ngyr_w: 0.00005       # gyroscope bias random work noise standard deviation.     #4.0e-5\ng_norm: 9.805         # gravity magnitude\n\n#unsynchronization parameters\nestimate_td: 1                      # online estimate time offset between camera and imu\ntd: 0                             # initial value of time offset. unit: s. readed image clock + td = real image clock (IMU clock)\n\n#loop closure parameters\nload_previous_pose_graph: 0        # load and reuse previous pose graph; load from 'pose_graph_save_path'\npose_graph_save_path: \"/home/dji/output/pose_graph/\" # save and load path\nsave_image: 0                   # save image in pose graph for visualization prupose; you can close this function by setting 0\n"
  },
  {
    "path": "config/vinsfusion/djirosimu_realsense_d435i/right.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: PINHOLE\ncamera_name: camera\nimage_width: 640\nimage_height: 480\ndistortion_parameters:\n   k1: 0.0\n   k2: 0.0\n   p1: 0.0\n   p2: 0.0\nprojection_parameters:\n   fx: 385.7544860839844\n   fy: 385.7544860839844\n   cx: 323.1204833984375\n   cy: 236.7432098388672\n"
  },
  {
    "path": "config/vinsfusion/euroc/cam0_pinhole.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: PINHOLE\ncamera_name: camera\nimage_width: 752\nimage_height: 480\ndistortion_parameters:\n   k1: -2.9545645106987750e-01\n   k2: 8.6623215640186171e-02\n   p1: 2.0132892276082517e-06\n   p2: 1.3924531371276508e-05\nprojection_parameters:\n   fx: 4.6115862106007575e+02\n   fy: 4.5975286598073296e+02\n   cx: 3.6265929181685937e+02\n   cy: 2.4852105668448124e+02\n"
  },
  {
    "path": "config/vinsfusion/euroc/cam1_pinhole.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: PINHOLE\ncamera_name: camera\nimage_width: 752\nimage_height: 480\ndistortion_parameters:\n   k1: -2.9294124381930947e-01\n   k2: 8.4798002331543665e-02\n   p1: -2.9984646536002372e-04\n   p2: 3.0028216325237329e-04\nprojection_parameters:\n   fx: 4.6009781682258682e+02\n   fy: 4.5890983492218902e+02\n   cx: 3.7314916359808268e+02\n   cy: 2.5440734973672119e+02\n"
  },
  {
    "path": "config/vinsfusion/euroc/euroc_stereo_imu_config.yaml",
    "content": "%YAML:1.0\n\n#common parameters\n#support: 1 imu 1 cam; 1 imu 2 cam: 2 cam;\nimu: 1\nnum_of_cam: 2\n\nimu_topic: \"/imu0\"\nimage0_topic: \"/cam0/image_raw\"\nimage1_topic: \"/cam1/image_raw\"\noutput_path: \"~/output/\"\n\n#cam0_calib: \"cam0_mei.yaml\"\n#cam1_calib: \"cam1_mei.yaml\"\ncam0_calib: \"cam0_pinhole.yaml\"\ncam1_calib: \"cam1_pinhole.yaml\"\nimage_width: 752\nimage_height: 480\n\n# contains the right_T_left aka 1_T_0, ie. stereo baseline. I need right_T_left that a transform that transforms 3d points of left to 3d points of right. in other words, if you sure of right the position of left camera.\n# This is an optional argument even if using `num_of_cam:=2`. If I cannot\n# find this key I will use `body_T_cam0` and `body_T_cam1` to compute cam1_T_cam0.\n# But if this key exists, we will use these values as stereo baseline.\n# **In this file, I assume translation re specified ****in mm**** (and not in meters).**\nextrinsic_1_T_0: \"extrinsics.yaml\"\n\n# Extrinsic parameter between IMU and Camera.\nestimate_extrinsic: 0   # 0  Have an accurate extrinsic parameters. We will trust the following imu^R_cam, imu^T_cam, don't change it.\n                        # 1  Have an initial guess about extrinsic parameters. We will optimize around your initial guess.\n\nbody_T_cam0: !!opencv-matrix\n   rows: 4\n   cols: 4\n   dt: d\n   data: [0.0148655429818, -0.999880929698, 0.00414029679422, -0.0216401454975,\n           0.999557249008, 0.0149672133247, 0.025715529948,  -0.064676986768,\n           -0.0257744366974, 0.00375618835797, 0.999660727178, 0.00981073058949,\n           0, 0, 0, 1]\n\nbody_T_cam1: !!opencv-matrix\n   rows: 4\n   cols: 4\n   dt: d\n   data: [0.0125552670891, -0.999755099723, 0.0182237714554, -0.0198435579556,\n           0.999598781151, 0.0130119051815, 0.0251588363115, 0.0453689425024,\n          -0.0253898008918, 0.0179005838253, 0.999517347078, 0.00786212447038,\n          0, 0, 0, 1]\n\n#Multiple thread support\nmultiple_thread: 1\n\n#feature traker paprameters\nmax_cnt: 150            # max feature number in feature tracking\nmin_dist: 30            # min distance between two features\nfreq: 10                # frequence (Hz) of publish tracking result. At least 10Hz for good estimation. If set 0, the frequence will be same as raw image\nF_threshold: 1.0        # ransac threshold (pixel)\nshow_track: 1           # publish tracking image as topic\nflow_back: 0            # perform forward and backward optical flow to improve feature tracking accuracy\n\n#optimization parameters\nmax_solver_time: 0.04  # max solver itration time (ms), to guarantee real time\nmax_num_iterations: 8   # max solver itrations, to guarantee real time\nkeyframe_parallax: 10.0 # keyframe selection threshold (pixel)\n\n#imu parameters       The more accurate parameters you provide, the better performance\nacc_n: 0.1          # accelerometer measurement noise standard deviation.\ngyr_n: 0.01         # gyroscope measurement noise standard deviation.\nacc_w: 0.001        # accelerometer bias random work noise standard deviation.\ngyr_w: 0.0001       # gyroscope bias random work noise standard deviation.\ng_norm: 9.81007     # gravity magnitude\n\n#unsynchronization parameters\nestimate_td: 0                      # online estimate time offset between camera and imu\ntd: 0.0                             # initial value of time offset. unit: s. readed image clock + td = real image clock (IMU clock)\n\n#loop closure parameters\nload_previous_pose_graph: 0        # load and reuse previous pose graph; load from 'pose_graph_save_path'\npose_graph_save_path: \"~/output/pose_graph/\" # save and load path\nsave_image: 1                   # save image in pose graph for visualization prupose; you can close this function by setting 0\n"
  },
  {
    "path": "config/vinsfusion/euroc/extrinsics.yaml",
    "content": "%YAML:1.0\n---\n    # this is computed for euric using body_T_cam0 and body_T_cam1.\ntransform:\n   q_x: -0.00704531\n   q_y: 0.000179855\n   q_z: -0.00115733\n   q_w: 0.999974\n   t_x: -110.074\n   t_y: 0.399122\n   t_z: -0.853703\n"
  },
  {
    "path": "config/vinsfusion/mynteye/camera_left.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: KANNALA_BRANDT\ncamera_name: camera_left\nimage_width: 752\nimage_height: 480\nprojection_parameters:\n   k2: -1.9372136316513616e-02\n   k3: -9.8140934466631590e-03\n   k4: 5.2090950264238800e-03\n   k5: -2.5384805224148016e-03\n   mu: 3.6779833417894611e+02\n   mv: 3.6735717027889308e+02\n   u0: 3.7150938938370604e+02\n   v0: 2.3994369620747401e+02\n"
  },
  {
    "path": "config/vinsfusion/mynteye/camera_left_pinhole.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: PINHOLE\ncamera_name: camera_left\nimage_width: 752\nimage_height: 480\ndistortion_parameters:\n   k1: -3.0304011877067349e-01\n   k2: 7.8611376314970768e-02\n   p1: 1.3200427858719645e-03\n   p2: -1.2942361409192899e-03\nprojection_parameters:\n   fx: 3.7560167589977470e+02\n   fy: 3.7552589588133230e+02\n   cx: 3.7887177487131697e+02\n   cy: 2.3402525474822104e+02\n"
  },
  {
    "path": "config/vinsfusion/mynteye/camera_right.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: KANNALA_BRANDT\ncamera_name: camera_right\nimage_width: 752\nimage_height: 480\nprojection_parameters:\n   k2: -1.6877708507279571e-02\n   k3: -2.2125250367976849e-02\n   k4: 2.5629804328110812e-02\n   k5: -1.3352680211095494e-02\n   mu: 3.6746513955323167e+02\n   mv: 3.6703089274525593e+02\n   u0: 3.6200946050656484e+02\n   v0: 2.4919721516867821e+02\n"
  },
  {
    "path": "config/vinsfusion/mynteye/camera_right_pinhole.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: PINHOLE\ncamera_name: camera_right\nimage_width: 752\nimage_height: 480\ndistortion_parameters:\n   k1: -3.0790744874410708e-01\n   k2: 8.3037204977051693e-02\n   p1: 1.1347651021695062e-03\n   p2: -7.8671202943393290e-04\nprojection_parameters:\n   fx: 3.7573448135250288e+02\n   fy: 3.7536464618331070e+02\n   cx: 3.6634215933104940e+02\n   cy: 2.4325470902936044e+02\n"
  },
  {
    "path": "config/vinsfusion/mynteye/extrinsics.yaml",
    "content": "%YAML:1.0\n---\ntransform:\n   q_x: -7.0955103716253032e-04\n   q_y: -1.5775578725333590e-03\n   q_z: -1.2732644461763854e-03\n   q_w: 9.9999769332040711e-01\n   t_x: -1.2006984141573309e+02\n   t_y: 3.3956264524978619e-01\n   t_z: -1.6784055634087214e-01\n"
  },
  {
    "path": "config/vinsfusion/mynteye/mynteye_stereo_imu_config.yaml",
    "content": "%YAML:1.0\n\n#common parameters\n#support: 1 imu 1 cam; 1 imu 2 cam: 2 cam;\nimu: 1\nnum_of_cam: 2\n\nimu_topic: \"/mynteye/imu/data_raw\"\nimage0_topic: \"/mynteye/left/image_raw\"\nimage1_topic: \"/mynteye/right/image_raw\"\noutput_path: \"/Bulk_Data/vins-mono-output/\"\n\n#cam0_calib: \"camera_left_pinhole.yaml\"\n#cam1_calib: \"camera_right_pinhole.yaml\"\ncam0_calib: \"camera_left.yaml\"\ncam1_calib: \"camera_right.yaml\"\nimage_width: 752\nimage_height: 480\n\n# contains the right_T_left aka 1_T_0, ie. stereo baseline. I need right_T_left that a transform that transforms 3d points of left to 3d points of right. in other words, if you sure of right the position of left camera.\n# This is an optional argument even if using `num_of_cam:=2`. If I cannot\n# find this key I will use `body_T_cam0` and `body_T_cam1` to compute cam1_T_cam0.\n# But if this key exists, we will use these values as stereo baseline.\n# **In this file, I assume translation re specified ****in mm**** (and not in meters).**\nextrinsic_1_T_0: \"extrinsics.yaml\"\n\n# Extrinsic parameter between IMU and Camera.\nestimate_extrinsic: 0   # 0  Have an accurate extrinsic parameters. We will trust the following imu^R_cam, imu^T_cam, don't change it.\n                        # 1  Have an initial guess about extrinsic parameters. We will optimize around your initial guess.\n\nbody_T_cam0: !!opencv-matrix\n   rows: 4\n   cols: 4\n   dt: d\n   data: [4.2739206953025244e-03, -9.9997227378122833e-01,-6.0979726705474944e-03,6.1040452082857912e-03,\n            9.9998919890819160e-01, 4.2626966744552242e-03, 1.8524265207998580e-03,-4.8408978130397302e-02,\n            -1.8263813521932205e-03, -6.1058239298286089e-03, 9.9997969141642784e-01,2.2594515206886469e-02,\n            0,0,0,1]\n\n#  ===> imu_T_cam0 * inv( cam1_T_cam0 )\nbody_T_cam1: !!opencv-matrix\n   rows: 4\n   cols: 4\n   dt: d\n   data: [ 0.00174443, -0.99998733, -0.0046694 ,  0.00665227,\n        0.99998578,  0.00172106,  0.00500326,  0.07165957,\n       -0.00499517, -0.00467806,  0.9999766 ,  0.02216417,\n        0.        ,  0.        ,  0.        ,  1.        ]\n\n#Multiple thread support\nmultiple_thread: 1\n\n#feature traker paprameters\nmax_cnt: 100            # max feature number in feature tracking\nmin_dist: 30            # min distance between two features\nfreq: 10                # frequence (Hz) of publish tracking result. At least 10Hz for good estimation. If set 0, the frequence will be same as raw image\nF_threshold: 1.1        # ransac threshold (pixel)\nshow_track: 0           # publish tracking image as topic\nflow_back: 1            # perform forward and backward optical flow to improve feature tracking accuracy\n\n#optimization parameters\nmax_solver_time: 0.05  # max solver itration time (ms), to guarantee real time\nmax_num_iterations: 12   # max solver itrations, to guarantee real time\nkeyframe_parallax: 10.0 # keyframe selection threshold (pixel)\n\n#imu parameters       The more accurate parameters you provide, the better performance\nacc_n: 0.04          # accelerometer measurement noise standard deviation. #0.2   0.04\ngyr_n: 0.004         # gyroscope measurement noise standard deviation.     #0.05  0.004\nacc_w: 0.0004         # accelerometer bias random work noise standard deviation.  #0.02\ngyr_w: 2.0e-5       # gyroscope bias random work noise standard deviation.     #4.0e-5\ng_norm: 9.80766     # gravity magnitude\n\n#unsynchronization parameters\nestimate_td: 0                      # online estimate time offset between camera and imu\ntd: 0.0                             # initial value of time offset. unit: s. readed image clock + td = real image clock (IMU clock)\n\n#loop closure parameters\nload_previous_pose_graph: 0        # load and reuse previous pose graph; load from 'pose_graph_save_path'\npose_graph_save_path: \"~/output/pose_graph/\" # save and load path\nsave_image: 0                   # save image in pose graph for visualization prupose; you can close this function by setting 0\n"
  },
  {
    "path": "config/vinsfusion/realsense_d435i/extrinsics.yaml",
    "content": "%YAML:1.0\n---\ntransform:\n   q_x: 0.\n   q_y: 0.\n   q_z: 0.\n   q_w: 1.\n   t_x: -49.8\n   t_y: 0.\n   t_z: 0.\n#4.98 cm for the realsense d435i device. This device has hardware undistort so can safely use the pinhole model \n"
  },
  {
    "path": "config/vinsfusion/realsense_d435i/left.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: PINHOLE\ncamera_name: camera\nimage_width: 640\nimage_height: 480\ndistortion_parameters:\n   k1: 0.0\n   k2: 0.0\n   p1: 0.0\n   p2: 0.0\nprojection_parameters:\n   fx: 385.7544860839844\n   fy: 385.7544860839844\n   cx: 323.1204833984375\n   cy: 236.7432098388672\n"
  },
  {
    "path": "config/vinsfusion/realsense_d435i/realsense_stereo_imu_config.yaml",
    "content": "%YAML:1.0\n\n#common parameters\n#support: 1 imu 1 cam; 1 imu 2 cam: 2 cam;\nimu: 1\nnum_of_cam: 2\n\n#imu_topic: \"/camera/imu\"\nimu_topic: \"/djiros/imu\"\nimage0_topic: \"/camera/infra1/image_rect_raw\"\nimage1_topic: \"/camera/infra2/image_rect_raw\"\noutput_path: \"/home/dji/output/\"\n\ncam0_calib: \"left.yaml\"\ncam1_calib: \"right.yaml\"\nimage_width: 640\nimage_height: 480\n\n\n\n# contains the right_T_left aka 1_T_0, ie. stereo baseline. I need right_T_left that a transform that transforms 3d points of left to 3d points of right. in other words, if you sure of right the position of left camera.\n# This is an optional argument even if using `num_of_cam:=2`. If I cannot\n# find this key I will use `body_T_cam0` and `body_T_cam1` to compute cam1_T_cam0.\n# But if this key exists, we will use these values as stereo baseline.\n# **In this file, I assume translation re specified ****in mm**** (and not in meters).**\nextrinsic_1_T_0: \"extrinsics.yaml\"\n\n\n# Extrinsic parameter between IMU and Camera.\nestimate_extrinsic: 1   # 0  Have an accurate extrinsic parameters. We will trust the following imu^R_cam, imu^T_cam, don't change it.\n                        # 1  Have an initial guess about extrinsic parameters. We will optimize around your initial guess.\n\nbody_T_cam0: !!opencv-matrix\n   rows: 4\n   cols: 4\n   dt: d\n   data: [ -1.0406548080980293e-02, -6.8339359975994052e-04,\n       9.9994561688635142e-01, 5.2600599061337287e-02,\n       -9.9994461366801901e-01, -1.5656625000493030e-03,\n       -1.0407607662295648e-02, 2.5510536758700533e-02,\n       1.5726898469127781e-03, -9.9999854083599726e-01,\n       -6.6706260700977182e-04, 9.5887981235633201e-03, 0., 0., 0., 1. ]\n\n\n\nbody_T_cam1: !!opencv-matrix\n   rows: 4\n   cols: 4\n   dt: d\n   data: [ -1.3185778504660295e-02, 1.2513252883544768e-04,\n       9.9991305601390978e-01, 4.2975722207919849e-02,\n       -9.9984703667423980e-01, 1.1490149582040754e-02,\n       -1.3186345829069746e-02, -5.4397185804457168e-02,\n       -1.1490800623434938e-02, -9.9993397822277874e-01,\n       -2.6393179226591457e-05, 1.8167602224138771e-02, 0., 0., 0., 1. ]\n\n#Multiple thread support\nmultiple_thread: 1\n\n#feature traker paprameters\nmax_cnt: 100            # max feature number in feature tracking\nmin_dist: 30            # min distance between two features\nfreq: 10                # frequence (Hz) of publish tracking result. At least 10Hz for good estimation. If set 0, the frequence will be same as raw image\nF_threshold: 1.0        # ransac threshold (pixel)\nshow_track: 0           # publish tracking image as topic\nflow_back: 1            # perform forward and backward optical flow to improve feature tracking accuracy\n\n#optimization parameters\nmax_solver_time: 0.04  # max solver itration time (ms), to guarantee real time\nmax_num_iterations: 8   # max solver itrations, to guarantee real time\nkeyframe_parallax: 10.0 # keyframe selection threshold (pixel)\n\n#imu parameters       The more accurate parameters you provide, the better performance\nacc_n: 0.2          # accelerometer measurement noise standard deviation. #0.2   0.04\ngyr_n: 0.02         # gyroscope measurement noise standard deviation.     #0.05  0.004\nacc_w: 0.001         # accelerometer bias random work noise standard deviation.  #0.002\ngyr_w: 0.0001       # gyroscope bias random work noise standard deviation.     #4.0e-5\ng_norm: 9.805         # gravity magnitude\n\n#unsynchronization parameters\nestimate_td: 0                      # online estimate time offset between camera and imu\ntd: 0.00                             # initial value of time offset. unit: s. readed image clock + td = real image clock (IMU clock)\n\n#loop closure parameters\nload_previous_pose_graph: 0        # load and reuse previous pose graph; load from 'pose_graph_save_path'\npose_graph_save_path: \"/home/dji/output/pose_graph/\" # save and load path\nsave_image: 0                   # save image in pose graph for visualization prupose; you can close this function by setting 0\n"
  },
  {
    "path": "config/vinsfusion/realsense_d435i/right.yaml",
    "content": "%YAML:1.0\n---\nmodel_type: PINHOLE\ncamera_name: camera\nimage_width: 640\nimage_height: 480\ndistortion_parameters:\n   k1: 0.0\n   k2: 0.0\n   p1: 0.0\n   p2: 0.0\nprojection_parameters:\n   fx: 385.7544860839844\n   fy: 385.7544860839844\n   cx: 323.1204833984375\n   cy: 236.7432098388672\n"
  },
  {
    "path": "config/vinsmono_debug_config.yaml",
    "content": "%YAML:1.0\n\n#######\n## This is config file for running vins-mono. We use this for imu_T_camera calibration.\n## Set the estimate_extrinsic to 2. If vins mono works on, just copy (written in `output_path`)\n#######\n\n# imu_topic: \"/camera/imu\"\nimu_topic: \"/djiros/imu\"\nimage_topic: \"/camera/infra1/image_rect_raw\"\n#image_topic: \"/camera/infra2/image_rect_raw\"\n\n# output_path: \"/home/tony-ws1/output/\"\noutput_path: \"/home/dji/output/\"\n\n#camera calibration - left , right same for realsense\nmodel_type: PINHOLE\ncamera_name: camera\nimage_width: 640\nimage_height: 480\ndistortion_parameters:\n   k1: 0.0\n   k2: 0.0\n   p1: 0.0\n   p2: 0.0\nprojection_parameters:\n   fx: 385.7544860839844\n   fy: 385.7544860839844\n   cx: 323.1204833984375\n   cy: 236.7432098388672\n\n\n# Extrinsic parameter between IMU and Camera.\nestimate_extrinsic: 2   # 0  Have an accurate extrinsic parameters. We will trust the following imu^R_cam, imu^T_cam, don't change it.\n                        # 1  Have an initial guess about extrinsic parameters. We will optimize around your initial guess.\n                        # 2  Don't know anything about extrinsic parameters. You don't need to give R,T. We will try to calibrate it. Do some rotation movement at beginning.\n#If you choose 0 or 1, you should write down the following matrix.\n#Rotation from camera frame to imu frame, imu^R_cam\nextrinsicRotation: !!opencv-matrix\n   rows: 3\n   cols: 3\n   dt: d\n   data: [ -1.0406548080980293e-02, -6.8339359975994052e-04,\n       9.9994561688635142e-01, -9.9994461366801901e-01,\n       -1.5656625000493030e-03, -1.0407607662295648e-02,\n       1.5726898469127781e-03, -9.9999854083599726e-01,\n       -6.6706260700977182e-04 ]\nextrinsicTranslation: !!opencv-matrix\n   rows: 3\n   cols: 1\n   dt: d\n   data: [ 5.2600599061337287e-02, 2.5510536758700533e-02,\n       9.5887981235633201e-03 ]\n\n\n#feature traker paprameters\nmax_cnt: 200            # max feature number in feature tracking\nmin_dist: 25            # min distance between two features\nfreq: 15                # frequence (Hz) of publish tracking result. At least 10Hz for good estimation. If set 0, the frequence will be same as raw image\nF_threshold: 1.0        # ransac threshold (pixel)\nshow_track: 1           # publish tracking image as topic\nequalize: 1             # if image is too dark or light, trun on equalize to find enough features\nfisheye: 0              # if using fisheye, trun on it. A circle mask will be loaded to remove edge noisy points\n\n#optimization parameters\nmax_solver_time: 0.04  # max solver itration time (ms), to guarantee real time\nmax_num_iterations: 8   # max solver itrations, to guarantee real time\nkeyframe_parallax: 10.0 # keyframe selection threshold (pixel)\n\n#imu parameters       The more accurate parameters you provide, the better performance\nacc_n: 0.05          # accelerometer measurement noise standard deviation. #0.2   0.04\ngyr_n: 0.01         # gyroscope measurement noise standard deviation.     #0.05  0.004\nacc_w: 0.002         # accelerometer bias random work noise standard deviation.  #0.02\ngyr_w: 2.0e-5       # gyroscope bias random work noise standard deviation.     #4.0e-5\ng_norm: 9.805     # gravity magnitude\n\n#loop closure parameters\nloop_closure: 0                    # start loop closure\nload_previous_pose_graph: 0        # load and reuse previous pose graph; load from 'pose_graph_save_path'\nfast_relocalization: 0             # useful in real-time and large project\npose_graph_save_path: \"/home/tony-ws1/output/pose_graph/\" # save and load path\n\n#unsynchronization parameters\nestimate_td: 1                      # online estimate time offset between camera and imu\ntd: 0.038                             # initial value of time offset. unit: s. readed image clock + td = real image clock (IMU clock)\n\n#rolling shutter parameters\nrolling_shutter: 0                  # 0: global shutter camera, 1: rolling shutter camera\nrolling_shutter_tr: 0               # unit: s. rolling shutter read out time per frame (from data sheet).\n\n#visualization parameters\nsave_image: 1                   # save image in pose graph for visualization prupose; you can close this function by setting 0\nvisualize_imu_forward: 0        # output imu forward propogation to achieve low latency and high frequence results\nvisualize_camera_size: 0.4      # size of camera marker in RVIZ\n"
  },
  {
    "path": "launch/debugging_vinsmono.launch",
    "content": "<launch>\n    <arg name=\"config_path\" default = \"$(find cerebro)/config/vinsmono_debug_config.yaml\" />\n\t  <arg name=\"vins_path\" default = \"$(find feature_tracker)/../config/../\" />\n\n    <node name=\"feature_tracker\" pkg=\"feature_tracker\" type=\"feature_tracker\" output=\"log\">\n        <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n        <param name=\"vins_folder\" type=\"string\" value=\"$(arg vins_path)\" />\n    </node>\n\n    <node name=\"vins_estimator\" pkg=\"vins_estimator\" type=\"vins_estimator\" output=\"screen\">\n       <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n       <param name=\"vins_folder\" type=\"string\" value=\"$(arg vins_path)\" />\n    </node>\n\n    <!-- <node name=\"pose_graph\" pkg=\"pose_graph\" type=\"pose_graph\" output=\"screen\">\n        <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n        <param name=\"visualization_shift_x\" type=\"int\" value=\"0\" />\n        <param name=\"visualization_shift_y\" type=\"int\" value=\"0\" />\n        <param name=\"skip_cnt\" type=\"int\" value=\"0\" />\n        <param name=\"skip_dis\" type=\"double\" value=\"0\" />\n    </node> -->\n\n</launch>\n"
  },
  {
    "path": "launch/euroc_vinsfusion.launch",
    "content": "<launch timeout=\"100.0\">\n\n    <!-- CONFIG FILE -->\n    <!-- <arg name=\"config_path\" default=\"$(find cerebro)/config/vinsfusion/euroc/euroc_mono_imu_config.yaml\" /> -->\n    <arg name=\"config_path\" default=\"$(find cerebro)/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml\" />\n\n\n\n    <!-- BAG -->\n    <arg name=\"bag_path\" default=\"/Bulk_Data/ros_bags/euroc/\" />\n\n    <!-- <arg name=\"bag_file\" default=\"MH_01_easy.bag\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"MH_02_easy.bag\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"MH_03_medium.bag\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"MH_04_difficult.bag\" /> -->\n    <arg name=\"bag_file\" default=\"MH_05_difficult.bag\" />\n    <!-- <arg name=\"bag_file\" default=\"V1_01_easy.bag\" /> -->\n    <!--<arg name=\"bag_file\" default=\"V1_02_medium.bag\" />-->\n    <!--<arg name=\"bag_file\" default=\"V1_03_difficult.bag\" />-->\n    <!-- <arg name=\"bag_file\" default=\"V2_01_easy.bag\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"V2_02_medium.bag\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"V2_03_difficult.bag\" /> -->\n\n    <!-- <node pkg=\"rosbag\" type=\"play\" name=\"rosbag\" args=\"$(arg bag_path)/$(arg bag_file) -s 35\" output=\"log\"/> -->\n    <!-- END Bag -->\n\n\n    <!-- VINS-Fusion Odometry Estimator -->\n    <!-- unlike previous vins-mono, even the feature tracker is put inside 1 node ie. vins_node.\n    $(ubuntu) rosrun vins vins_node /app/catkin_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml\n    -->\n    <node name=\"vins_estimator\" pkg=\"vins\" type=\"vins_node\" args=\" $(arg config_path)\" output=\"log\">\n    </node>\n\n\n    <group if=\"0\">\n        <!-- VINS-Fusion loop_fusion -->\n        <!-- This is the DBOW + posegraph optimization from Qin Tong\n        $(ubuntu) rosrun loop_fusion loop_fusion_node /app/catkin_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml\n        -->\n        <node name=\"loop_fusion\" pkg=\"loop_fusion\" type=\"loop_fusion_node\" args=\" $(arg config_path)\" output=\"log\">\n        </node>\n    </group>\n\n\n    <group if=\"1\">\n        <!-- Cerebro -->\n        <node name=\"cerebro_node\" pkg=\"cerebro\" type=\"cerebro_node\" output=\"log\">\n           <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n        </node>\n\n        <!-- KERAS SERVER -->\n        <!-- $(ubuntu) rosrun cerebro whole_image_desc_compute_server.py _config_file:=/app/catk_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml -->\n        <node name=\"my_desc_server\" pkg=\"cerebro\" type=\"whole_image_desc_compute_server.py\" output=\"screen\">\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"/models.keras/Apr2019/gray_conv6_K16Ghost1__centeredinput/core_model.500.keras\" /> -->\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/Apr2019/gray_conv6_K16__centeredinput/core_model.1000.keras\" /> -->\n            <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/mobilenet_conv7_allpairloss.keras\" />\n\n\n           <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n           <!--         OR -->\n           <!-- <param name=\"nrows\" type=\"string\" value=\"480\" />\n           <param name=\"ncols\" type=\"string\" value=\"752\" />\n           <param name=\"nchnls\" type=\"string\" value=\"1\" /> -->\n           <param name=\"nchnls\" type=\"string\" value=\"3\" />\n        </node>\n\n        <!-- Pose graph (kidnap aware) -->\n        <!-- $(ubuntu) rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam -->\n        <node name=\"keyframe_pose_graph_slam_node\" pkg=\"solve_keyframe_pose_graph\" type=\"keyframe_pose_graph_slam\" output=\"log\" >\n        </node>\n    </group>\n\n\n</launch>\n"
  },
  {
    "path": "launch/keras_server.launch",
    "content": "<launch>\n\n\n<!-- <arg name=\"config_path\" default=\"$(find cerebro)/config/euroc/euroc_config.yaml\" /> -->\n<!-- <arg name=\"config_path\" default=\"$(find cerebro)/config/blackbox4/blackbox4_config.yaml\" /> -->\n<!-- <arg name=\"config_path\" default=\"$(find cerebro)/config/tum_vi/tum_jie_config.yaml\" /> -->\n<arg name=\"config_path\" default=\"$(find cerebro)/config/mynteye/mynteye_config.yaml\" />\n<!-- <arg name=\"config_path\" default=\"$(find cerebro)/config/vinsfusion/euroc/cam0_mei.yaml\" />  -->\n<!-- <arg name=\"config_path\" default=\"$(find cerebro)/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml\" /> -->\n\n\n\n<node name=\"my_desc_server\" pkg=\"cerebro\" type=\"whole_image_desc_compute_server.py\" output=\"screen\">\n    <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"/models.keras/Apr2019/gray_conv6_K16Ghost1__centeredinput/core_model.500.keras\" /> -->\n    <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/Apr2019/gray_conv6_K16__centeredinput/core_model.1000.keras\" /> -->\n    <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/mobilenet_conv7_allpairloss.keras\" />\n\n   <!-- <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" /> -->\n   <!--         OR -->\n   <param name=\"nrows\" type=\"string\" value=\"480\" />\n   <param name=\"ncols\" type=\"string\" value=\"752\" />\n   <param name=\"nchnls\" type=\"string\" value=\"3\" />\n\n</node>\n</launch>\n"
  },
  {
    "path": "launch/mynteye_vinsfusion.launch",
    "content": "<launch timeout=\"100.0\">\n\n    <!-- CONFIG FILE -->\n    <arg name=\"config_path\" default=\"$(find cerebro)/config/vinsfusion/mynteye/mynteye_stereo_imu_config.yaml\" />\n\n\n    <!-- BAG -->\n    <arg name=\"bag_path\" default=\"/Bulk_Data/ros_bags/mynteye/\" />\n\n    <!-- ### IN LAB -->\n    <!-- <arg name=\"bag_file\" default=\"1.bag\" doc=\"sitting on my desk\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-20-17-45-55.bag\" doc=\"single loop in lab\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-20-17-42-54.bag\" doc=\"multiple loops in lab\"/> -->\n    <arg name=\"bag_file\" default=\"2019-01-28-16-32-23.bag\" doc=\"many-10 loops in lab\"/>\n    <!-- <arg name=\"bag_file\" default=\"2018-11-22-12-06-47.bag\" doc=\"drone fly area loopy\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-22-12-09-21.bag\" doc=\"conference room RI\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-12-37-49.bag\" doc=\"lab rotated loops\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-10-24.bag\" doc=\"outside RI\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2019-01-14-18-25-18.bag\" doc=\"lab AR test\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2019-06-26-16-09-04.bag\" doc=\"seq0, 1 loop. loop-0: walk in lab; lap-1: same place with 90 degree in place rotation\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2019-06-26-16-19-43.bag\" doc=\"seq1,1 loop. loop-0: walk in lab; lap-1: same place with 90 degree in place rotation\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2019-06-26-16-36-20.bag\" doc=\"seq2,1 loop. loop-0: walk in lab +45 degrees; lap-1: same place with -45degres in place rotation\"/> -->\n\n    <!-- ### In HKUST Academic block and CYT UG -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-13-44.bag\" doc=\"CYT UG floor\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-22-50.bag\" doc=\"academic concourse, no loop in this\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-30-39.bag\" doc=\"near coffee shop 1\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-36-41.bag\" doc=\"seng\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-12-13-16-48-46.bag\" doc=\"seng3. walking on academic concouse CYT end, walking down the escalator and back up.\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-12-13-16-56-36.bag\" doc=\"concourse-seng4. from coffee-shop to atrium\"/> -->\n\n    <!-- ### Kidnap sequences in lab -->\n    <!-- <arg name=\"bag_path\" default=\"/Bulk_Data/ros_bags/mynteye/\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-01-14-17-20-06.bag\" doc=\"lab kidnap. easy\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-02-14-12-00-21.bag\" doc=\"3 kidnaps in lab. This gives the chained case for pose computation between worlds\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-02-14-17-19-29.bag\" doc=\"4 kidnaps in lab\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-02-14-17-19-29.bag\" doc=\"4 kidnaps in lab\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-02-21-17-10-30.bag\" doc=\"0:-; 1:-; 2:merge(0), merge(1). 2nd merges with both 0 and 1.\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-02-21-17-38-58.bag\" doc=\"0:-; 1:-; 2:merge(0), merge(1). 2nd merges with both 0 and 1.\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-03-07-14-58-00.bag\" doc=\"2 independent runs\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-03-14-16-12-22.bag\" doc=\"7 kidnaps. 0:; 1:; all next merges with 1. Final merge with 0 and 3.\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-03-14-16-36-58.bag\" doc=\"10 kidnaps. 0; 1; merge everything with 1. finall all merge to zero. very complicated dependence structure of the walk,\"/> -->\n\n\n    <!-- ### Mall -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-08-16-02-10.bag\" doc=\"\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-08-16-09-41.bag\" doc=\"good. no kidnap, but just 1 loop starting from Muji\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-08-16-14-19.bag\" doc=\"1 moderate loop;kidnap;a very short seq in a portion of the loop. near marathon sports, (shoes shops)\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-08-16-17-05.bag\" doc=\"corridor near shoes shop;kidnap;\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-07-51.bag\" doc=\"3 kidnaps. k1:hsbc;right to muji; shoes;;k1: panash;atrium;shoes;panash\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-17-34.bag\" doc=\"2 kidnaps. k1:fancl;bossino;muji;right of hsbc rotated and tilted; k2:bodyshop ; k3: fancl;bossini,shoes\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-40-35.bag\" doc=\"level-1 from from PacificC to muji to hsbc. 1kidnap\" /> -->\n\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-23-38.bag\" doc=\"start from atrium near pacific-coffee;go up; casual walking on top floor 4 kidnaps\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-34-01.bag\" doc=\"1 corridor on top floor. no loop no kidnap\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-34-57.bag\" doc=\"1 kidnaps. both runs r independent, no interworld revisit\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-38-51.bag\" doc=\"really short (abt 30 sec near broadway)\" /> -->\n\n\n\n\n\n\n    <node pkg=\"rosbag\" type=\"play\" name=\"rosbag\" args=\"$(arg bag_path)/$(arg bag_file) -s 35 -d 10\" output=\"log\"/>\n\n\n    <!-- END Bag -->\n\n\n    <!-- VINS-Fusion Estimator -->\n    <!-- unlike previous vins-mono, even the feature tracker is put inside 1 node ie. vins_node.\n    $(ubuntu) rosrun vins vins_node /app/catkin_ws/src/cerebro/config/vinsfusion/mynteye/mynteye_stereo_imu_config.yaml\n    -->\n    <node name=\"vins_estimator\" pkg=\"vins\" type=\"vins_node\" args=\" $(arg config_path)\" output=\"log\">\n    </node>\n\n    <group if=\"0\">\n        <!-- VINS-Fusion loop_fusion -->\n        <!-- This is the DBOW + posegraph optimization from Qin Tong\n        $(ubuntu) rosrun loop_fusion loop_fusion_node /app/catkin_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml\n        -->\n        <node name=\"loop_fusion\" pkg=\"loop_fusion\" type=\"loop_fusion_node\" args=\" $(arg config_path)\" output=\"log\">\n        </node>\n    </group>\n\n\n    <group if=\"1\">\n        <!-- Cerebro -->\n        <node name=\"cerebro_node\" pkg=\"cerebro\" type=\"cerebro_node\" output=\"screen\">\n           <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n        </node>\n\n        <!-- KERAS SERVER -->\n        <!-- $(ubuntu) rosrun cerebro whole_image_desc_compute_server.py _config_file:=/app/catk_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml -->\n        <node name=\"my_desc_server\" pkg=\"cerebro\" type=\"whole_image_desc_compute_server.py\" output=\"log\">\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/modelarch_and_weights.h5\" /> -->\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"/models.keras/June2019/centeredinput-m1to1-240x320x3__mobilenet-conv_pw_6_relu__K16__allpairloss/modelarch_and_weights.500.h5\" /> -->\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x3__mobilenet-conv_pw_6_relu__K16__allpairloss/modelarch_and_weights.700.h5\" /> -->\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x3__mobilenetv2-block_9_add__K16__allpairloss/modelarch_and_weights.800.h5\" />  -->\n            <param name=\"kerasmodel_file\" type=\"string\" value=\"/models.keras/June2019/centeredinput-m1to1-240x320x1__mobilenetv2-block_9_add__K16__allpairloss/modelarch_and_weights.2000.h5\" />\n\n\n           <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n           <!--         OR -->\n           <!--\n           <param name=\"nrows\" type=\"string\" value=\"480\" />\n           <param name=\"ncols\" type=\"string\" value=\"752\" />\n            -->\n\n            <!-- this is needed irrespective of the config_file or the nrows and ncols set manually here. -->\n           <param name=\"nchnls\" type=\"string\" value=\"1\" />\n           <!-- <param name=\"nchnls\" type=\"string\" value=\"3\" /> -->\n        </node>\n\n        <!-- Pose graph (kidnap aware) -->\n        <!-- $(ubuntu) rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam -->\n        <node name=\"keyframe_pose_graph_slam_node\" pkg=\"solve_keyframe_pose_graph\" type=\"keyframe_pose_graph_slam\" output=\"log\" >\n        </node>\n    </group>\n\n</launch>\n"
  },
  {
    "path": "launch/mynteye_vinsfusion_loadstate.launch",
    "content": "<launch timeout=\"100.0\">\n\n    <!-- CONFIG FILE -->\n    <arg name=\"config_path\" default=\"$(find cerebro)/config/vinsfusion/mynteye/mynteye_stereo_imu_config.yaml\" />\n\n\n    <!-- BAG -->\n    <arg name=\"bag_path\" default=\"/Bulk_Data/ros_bags/mynteye/\" />\n\n    <!-- ### IN LAB -->\n    <!-- <arg name=\"bag_file\" default=\"1.bag\" doc=\"sitting on my desk\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-20-17-45-55.bag\" doc=\"single loop in lab\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-20-17-42-54.bag\" doc=\"multiple loops in lab\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2019-01-28-16-32-23.bag\" doc=\"many-10 loops in lab\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-22-12-06-47.bag\" doc=\"drone fly area loopy\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-22-12-09-21.bag\" doc=\"conference room RI\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-12-37-49.bag\" doc=\"lab rotated loops\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-10-24.bag\" doc=\"outside RI\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2019-01-14-18-25-18.bag\" doc=\"lab AR test\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2019-06-26-16-09-04.bag\" doc=\"seq0, 1 loop. loop-0: walk in lab; lap-1: same place with 90 degree in place rotation\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2019-06-26-16-19-43.bag\" doc=\"seq1,1 loop. loop-0: walk in lab; lap-1: same place with 90 degree in place rotation\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2019-06-26-16-36-20.bag\" doc=\"seq2,1 loop. loop-0: walk in lab +45 degrees; lap-1: same place with -45degres in place rotation\"/> -->\n\n    <!-- ### In HKUST Academic block and CYT UG -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-13-44.bag\" doc=\"CYT UG floor\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-22-50.bag\" doc=\"academic concourse, no loop in this\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-30-39.bag\" doc=\"near coffee shop 1\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-36-41.bag\" doc=\"seng\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-12-13-16-48-46.bag\" doc=\"seng3. walking on academic concouse CYT end, walking down the escalator and back up.\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-12-13-16-56-36.bag\" doc=\"concourse-seng4. from coffee-shop to atrium\"/> -->\n\n    <!-- ### Kidnap sequences in lab -->\n    <!-- <arg name=\"bag_path\" default=\"/Bulk_Data/ros_bags/mynteye/\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-01-14-17-20-06.bag\" doc=\"lab kidnap. easy\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-02-14-12-00-21.bag\" doc=\"3 kidnaps in lab. This gives the chained case for pose computation between worlds\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-02-14-17-19-29.bag\" doc=\"4 kidnaps in lab\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-02-14-17-19-29.bag\" doc=\"4 kidnaps in lab\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-02-21-17-10-30.bag\" doc=\"0:-; 1:-; 2:merge(0), merge(1). 2nd merges with both 0 and 1.\"/> -->\n    <arg name=\"bag_file\" default=\"/kidnap/2019-02-21-17-38-58.bag\" doc=\"0:-; 1:-; 2:merge(0), merge(1). 2nd merges with both 0 and 1.\"/>\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-03-07-14-58-00.bag\" doc=\"2 independent runs\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-03-14-16-12-22.bag\" doc=\"7 kidnaps. 0:; 1:; all next merges with 1. Final merge with 0 and 3.\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-03-14-16-36-58.bag\" doc=\"10 kidnaps. 0; 1; merge everything with 1. finall all merge to zero. very complicated dependence structure of the walk,\"/> -->\n\n\n    <!-- ### Mall -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-08-16-02-10.bag\" doc=\"\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-08-16-09-41.bag\" doc=\"good. no kidnap, but just 1 loop starting from Muji\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-08-16-14-19.bag\" doc=\"1 moderate loop;kidnap;a very short seq in a portion of the loop. near marathon sports, (shoes shops)\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-08-16-17-05.bag\" doc=\"corridor near shoes shop;kidnap;\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-07-51.bag\" doc=\"3 kidnaps. k1:hsbc;right to muji; shoes;;k1: panash;atrium;shoes;panash\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-17-34.bag\" doc=\"2 kidnaps. k1:fancl;bossino;muji;right of hsbc rotated and tilted; k2:bodyshop ; k3: fancl;bossini,shoes\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-40-35.bag\" doc=\"level-1 from from PacificC to muji to hsbc. 1kidnap\" /> -->\n\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-23-38.bag\" doc=\"start from atrium near pacific-coffee;go up; casual walking on top floor 4 kidnaps\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-34-01.bag\" doc=\"1 corridor on top floor. no loop no kidnap\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-34-57.bag\" doc=\"1 kidnaps. both runs r independent, no interworld revisit\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-38-51.bag\" doc=\"really short (abt 30 sec near broadway)\" /> -->\n\n\n\n\n\n\n    <node pkg=\"rosbag\" type=\"play\" name=\"rosbag\" args=\"$(arg bag_path)/$(arg bag_file) -s 2 -d 10\" output=\"log\"/>\n\n\n    <!-- END Bag -->\n\n\n    <!-- VINS-Fusion Estimator -->\n    <!-- unlike previous vins-mono, even the feature tracker is put inside 1 node ie. vins_node.\n    $(ubuntu) rosrun vins vins_node /app/catkin_ws/src/cerebro/config/vinsfusion/mynteye/mynteye_stereo_imu_config.yaml\n    -->\n    <node name=\"vins_estimator\" pkg=\"vins\" type=\"vins_node\" args=\" $(arg config_path)\" output=\"log\">\n    </node>\n\n    <group if=\"0\">\n        <!-- VINS-Fusion loop_fusion -->\n        <!-- This is the DBOW + posegraph optimization from Qin Tong\n        $(ubuntu) rosrun loop_fusion loop_fusion_node /app/catkin_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml\n        -->\n        <node name=\"loop_fusion\" pkg=\"loop_fusion\" type=\"loop_fusion_node\" args=\" $(arg config_path)\" output=\"log\">\n        </node>\n    </group>\n\n\n    <group if=\"1\">\n        <!-- Cerebro -->\n\n                   <!-- NOTE:\n                        X) `config_file` Same as the vins-fusion, but you need to set cam=2 and imu=1 also you need to specify extrinsic_1_T_0 as I depend on stereo vision\n                        X) Specify the path for saving/loading state. Keep value as empty string to disable the function.\n                        For example if you kept loadStateFromDisk as empty string, I will not load aka fresh start.\n                        Similarly if you kept saveStateToDisk as empty, I will not write data to disk upon exit\n                    -->\n        <node name=\"cerebro_node\" pkg=\"cerebro\" type=\"cerebro_node\" output=\"log\">\n           <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n\n           <param name=\"loadStateFromDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_cerebro/\" />\n           <!-- <param name=\"loadStateFromDisk\" type=\"string\" value=\"\" /> -->\n           <!-- <param name=\"saveStateToDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_cerebro/\" /> -->\n           <param name=\"saveStateToDisk\" type=\"string\" value=\"\" />\n        </node>\n\n        <!-- KERAS SERVER -->\n        <!-- $(ubuntu) rosrun cerebro whole_image_desc_compute_server.py _config_file:=/app/catk_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml -->\n        <node name=\"my_desc_server\" pkg=\"cerebro\" type=\"whole_image_desc_compute_server.py\" output=\"screen\">\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/modelarch_and_weights.h5\" /> -->\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"/models.keras/June2019/centeredinput-m1to1-240x320x3__mobilenet-conv_pw_6_relu__K16__allpairloss/modelarch_and_weights.500.h5\" /> -->\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x3__mobilenet-conv_pw_6_relu__K16__allpairloss/modelarch_and_weights.700.h5\" /> -->\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x3__mobilenetv2-block_9_add__K16__allpairloss/modelarch_and_weights.800.h5\" />  -->\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"/models.keras/June2019/centeredinput-m1to1-240x320x1__mobilenetv2-block_9_add__K16__allpairloss/modelarch_and_weights.2000.h5\" /> -->\n            <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x1__mobilenetv2-block_9_add__K16__allpairloss/modelarch_and_weights.2000.h5\" />\n\n\n           <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n           <!--         OR -->\n           <!--\n           <param name=\"nrows\" type=\"string\" value=\"480\" />\n           <param name=\"ncols\" type=\"string\" value=\"752\" />\n            -->\n\n            <!-- this is needed irrespective of the config_file or the nrows and ncols set manually here. -->\n           <param name=\"nchnls\" type=\"string\" value=\"1\" />\n           <!-- <param name=\"nchnls\" type=\"string\" value=\"3\" /> -->\n        </node>\n\n        <!-- Pose graph (kidnap aware) -->\n        <!-- $(ubuntu) rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam -->\n        <node name=\"keyframe_pose_graph_slam_node\" pkg=\"solve_keyframe_pose_graph\" type=\"keyframe_pose_graph_slam\" output=\"log\" >\n            <param name=\"loadStateFromDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_posegraph_solver/\" />\n            <!-- <param name=\"loadStateFromDisk\" type=\"string\" value=\"\" /> -->\n            <!-- <param name=\"saveStateToDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_posegraph_solver/\" /> -->\n            <param name=\"saveStateToDisk\" type=\"string\" value=\"\" />\n        </node>\n    </group>\n\n</launch>\n"
  },
  {
    "path": "launch/mynteye_vinsfusion_ondrone.launch",
    "content": "<launch timeout=\"100.0\">\n\n    <!-- CONFIG FILE -->\n    <arg name=\"config_path\" default=\"$(find cerebro)/config/vinsfusion/mynteye/mynteye_stereo_imu_config.yaml\" />\n\n\n\n    <!-- VINS-Fusion Estimator -->\n    <!-- unlike previous vins-mono, even the feature tracker is put inside 1 node ie. vins_node.\n    $(ubuntu) rosrun vins vins_node /app/catkin_ws/src/cerebro/config/vinsfusion/mynteye/mynteye_stereo_imu_config.yaml\n    -->\n    <node name=\"vins_estimator\" pkg=\"vins\" type=\"vins_node\" args=\" $(arg config_path)\" output=\"log\">\n    </node>\n\n    <group if=\"0\">\n        <!-- VINS-Fusion loop_fusion -->\n        <!-- This is the DBOW + posegraph optimization from Qin Tong\n        $(ubuntu) rosrun loop_fusion loop_fusion_node /app/catkin_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml\n        -->\n        <node name=\"loop_fusion\" pkg=\"loop_fusion\" type=\"loop_fusion_node\" args=\" $(arg config_path)\" output=\"log\">\n        </node>\n    </group>\n\n\n    <group if=\"1\">\n        <!-- Cerebro -->\n        <node name=\"cerebro_node\" pkg=\"cerebro\" type=\"cerebro_node\" output=\"screen\">\n           <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n        </node>\n\n        <!-- KERAS SERVER -->\n        <!-- $(ubuntu) rosrun cerebro whole_image_desc_compute_server.py _config_file:=/app/catk_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml -->\n        <!-- Since I run the server on tx2 dont need to run it here -->\n        <group if=\"0\" >\n        <node name=\"my_desc_server\" pkg=\"cerebro\" type=\"whole_image_desc_compute_server.py\" output=\"screen\">\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/modelarch_and_weights.h5\" /> -->\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"/models.keras/June2019/centeredinput-m1to1-240x320x3__mobilenet-conv_pw_6_relu__K16__allpairloss/modelarch_and_weights.500.h5\" /> -->\n            <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x3__mobilenet-conv_pw_6_relu__K16__allpairloss/modelarch_and_weights.700.h5\" />\n\n\n           <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n           <!--         OR -->\n           <!--\n           <param name=\"nrows\" type=\"string\" value=\"480\" />\n           <param name=\"ncols\" type=\"string\" value=\"752\" />\n            -->\n\n            <!-- this is needed irrespective of the config_file or the nrows and ncols set manually here. -->\n           <!-- <param name=\"nchnls\" type=\"string\" value=\"1\" /> -->\n           <param name=\"nchnls\" type=\"string\" value=\"3\" />\n        </node>\n\n        <!-- Pose graph (kidnap aware) -->\n        <!-- $(ubuntu) rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam -->\n        <node name=\"keyframe_pose_graph_slam_node\" pkg=\"solve_keyframe_pose_graph\" type=\"keyframe_pose_graph_slam\" output=\"log\" >\n        </node>\n        </group>\n    </group>\n\n</launch>\n"
  },
  {
    "path": "launch/mynteye_vinsfusion_savestate.launch",
    "content": "<launch timeout=\"100.0\">\n\n    <!-- CONFIG FILE -->\n    <arg name=\"config_path\" default=\"$(find cerebro)/config/vinsfusion/mynteye/mynteye_stereo_imu_config.yaml\" />\n\n\n    <!-- BAG -->\n    <arg name=\"bag_path\" default=\"/Bulk_Data/ros_bags/mynteye/\" />\n\n    <!-- ### IN LAB -->\n    <!-- <arg name=\"bag_file\" default=\"1.bag\" doc=\"sitting on my desk\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-20-17-45-55.bag\" doc=\"single loop in lab\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-20-17-42-54.bag\" doc=\"multiple loops in lab\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2019-01-28-16-32-23.bag\" doc=\"many-10 loops in lab\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-22-12-06-47.bag\" doc=\"drone fly area loopy\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-22-12-09-21.bag\" doc=\"conference room RI\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-12-37-49.bag\" doc=\"lab rotated loops\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-10-24.bag\" doc=\"outside RI\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2019-01-14-18-25-18.bag\" doc=\"lab AR test\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2019-06-26-16-09-04.bag\" doc=\"seq0, 1 loop. loop-0: walk in lab; lap-1: same place with 90 degree in place rotation\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2019-06-26-16-19-43.bag\" doc=\"seq1,1 loop. loop-0: walk in lab; lap-1: same place with 90 degree in place rotation\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2019-06-26-16-36-20.bag\" doc=\"seq2,1 loop. loop-0: walk in lab +45 degrees; lap-1: same place with -45degres in place rotation\"/> -->\n\n    <!-- ### In HKUST Academic block and CYT UG -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-13-44.bag\" doc=\"CYT UG floor\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-22-50.bag\" doc=\"academic concourse, no loop in this\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-30-39.bag\" doc=\"near coffee shop 1\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-11-29-15-36-41.bag\" doc=\"seng\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-12-13-16-48-46.bag\" doc=\"seng3. walking on academic concouse CYT end, walking down the escalator and back up.\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"2018-12-13-16-56-36.bag\" doc=\"concourse-seng4. from coffee-shop to atrium\"/> -->\n\n    <!-- ### Kidnap sequences in lab -->\n    <!-- <arg name=\"bag_path\" default=\"/Bulk_Data/ros_bags/mynteye/\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-01-14-17-20-06.bag\" doc=\"lab kidnap. easy\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-02-14-12-00-21.bag\" doc=\"3 kidnaps in lab. This gives the chained case for pose computation between worlds\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-02-14-17-19-29.bag\" doc=\"4 kidnaps in lab\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-02-14-17-19-29.bag\" doc=\"4 kidnaps in lab\"/> -->\n    <arg name=\"bag_file\" default=\"/kidnap/2019-02-21-17-10-30.bag\" doc=\"0:-; 1:-; 2:merge(0), merge(1). 2nd merges with both 0 and 1.\"/>\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-02-21-17-38-58.bag\" doc=\"0:-; 1:-; 2:merge(0), merge(1). 2nd merges with both 0 and 1.\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-03-07-14-58-00.bag\" doc=\"2 independent runs\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-03-14-16-12-22.bag\" doc=\"7 kidnaps. 0:; 1:; all next merges with 1. Final merge with 0 and 3.\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/kidnap/2019-03-14-16-36-58.bag\" doc=\"10 kidnaps. 0; 1; merge everything with 1. finall all merge to zero. very complicated dependence structure of the walk,\"/> -->\n\n\n    <!-- ### Mall -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-08-16-02-10.bag\" doc=\"\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-08-16-09-41.bag\" doc=\"good. no kidnap, but just 1 loop starting from Muji\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-08-16-14-19.bag\" doc=\"1 moderate loop;kidnap;a very short seq in a portion of the loop. near marathon sports, (shoes shops)\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-08-16-17-05.bag\" doc=\"corridor near shoes shop;kidnap;\"/> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-07-51.bag\" doc=\"3 kidnaps. k1:hsbc;right to muji; shoes;;k1: panash;atrium;shoes;panash\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-17-34.bag\" doc=\"2 kidnaps. k1:fancl;bossino;muji;right of hsbc rotated and tilted; k2:bodyshop ; k3: fancl;bossini,shoes\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-40-35.bag\" doc=\"level-1 from from PacificC to muji to hsbc. 1kidnap\" /> -->\n\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-23-38.bag\" doc=\"start from atrium near pacific-coffee;go up; casual walking on top floor 4 kidnaps\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-34-01.bag\" doc=\"1 corridor on top floor. no loop no kidnap\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-34-57.bag\" doc=\"1 kidnaps. both runs r independent, no interworld revisit\" /> -->\n    <!-- <arg name=\"bag_file\" default=\"/mall/2019-04-10-15-38-51.bag\" doc=\"really short (abt 30 sec near broadway)\" /> -->\n\n\n\n\n\n\n    <node pkg=\"rosbag\" type=\"play\" name=\"rosbag\" args=\"$(arg bag_path)/$(arg bag_file) -s 2 -d 10\" output=\"log\"/>\n\n\n    <!-- END Bag -->\n\n\n    <!-- VINS-Fusion Estimator -->\n    <!-- unlike previous vins-mono, even the feature tracker is put inside 1 node ie. vins_node.\n    $(ubuntu) rosrun vins vins_node /app/catkin_ws/src/cerebro/config/vinsfusion/mynteye/mynteye_stereo_imu_config.yaml\n    -->\n    <node name=\"vins_estimator\" pkg=\"vins\" type=\"vins_node\" args=\" $(arg config_path)\" output=\"log\">\n    </node>\n\n    <group if=\"0\">\n        <!-- VINS-Fusion loop_fusion -->\n        <!-- This is the DBOW + posegraph optimization from Qin Tong\n        $(ubuntu) rosrun loop_fusion loop_fusion_node /app/catkin_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml\n        -->\n        <node name=\"loop_fusion\" pkg=\"loop_fusion\" type=\"loop_fusion_node\" args=\" $(arg config_path)\" output=\"log\">\n        </node>\n    </group>\n\n\n    <group if=\"1\">\n        <!-- Cerebro -->\n\n                   <!-- NOTE:\n                        X) `config_file` Same as the vins-fusion, but you need to set cam=2 and imu=1 also you need to specify extrinsic_1_T_0 as I depend on stereo vision\n                        X) Specify the path for saving/loading state. Keep value as empty string to disable the function.\n                        For example if you kept loadStateFromDisk as empty string, I will not load aka fresh start.\n                        Similarly if you kept saveStateToDisk as empty, I will not write data to disk upon exit\n                    -->\n        <node name=\"cerebro_node\" pkg=\"cerebro\" type=\"cerebro_node\" output=\"screen\">\n           <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n\n           <!-- <param name=\"loadStateFromDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_cerebro/\" /> -->\n           <param name=\"loadStateFromDisk\" type=\"string\" value=\"\" />\n           <param name=\"saveStateToDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_cerebro/\" />\n           <!-- <param name=\"saveStateToDisk\" type=\"string\" value=\"\" /> -->\n        </node>\n\n        <!-- KERAS SERVER -->\n        <!-- $(ubuntu) rosrun cerebro whole_image_desc_compute_server.py _config_file:=/app/catk_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml -->\n        <node name=\"my_desc_server\" pkg=\"cerebro\" type=\"whole_image_desc_compute_server.py\" output=\"log\">\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/modelarch_and_weights.h5\" /> -->\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"/models.keras/June2019/centeredinput-m1to1-240x320x3__mobilenet-conv_pw_6_relu__K16__allpairloss/modelarch_and_weights.500.h5\" /> -->\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x3__mobilenet-conv_pw_6_relu__K16__allpairloss/modelarch_and_weights.700.h5\" /> -->\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x3__mobilenetv2-block_9_add__K16__allpairloss/modelarch_and_weights.800.h5\" />  -->\n            <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"/models.keras/June2019/centeredinput-m1to1-240x320x1__mobilenetv2-block_9_add__K16__allpairloss/modelarch_and_weights.2000.h5\" /> -->\n            <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x1__mobilenetv2-block_9_add__K16__allpairloss/modelarch_and_weights.2000.h5\" />\n\n\n           <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n           <!--         OR -->\n           <!--\n           <param name=\"nrows\" type=\"string\" value=\"480\" />\n           <param name=\"ncols\" type=\"string\" value=\"752\" />\n            -->\n\n            <!-- this is needed irrespective of the config_file or the nrows and ncols set manually here. -->\n           <param name=\"nchnls\" type=\"string\" value=\"1\" />\n           <!-- <param name=\"nchnls\" type=\"string\" value=\"3\" /> -->\n        </node>\n\n        <!-- Pose graph (kidnap aware) -->\n        <!-- $(ubuntu) rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam -->\n        <node name=\"keyframe_pose_graph_slam_node\" pkg=\"solve_keyframe_pose_graph\" type=\"keyframe_pose_graph_slam\" output=\"log\" >\n            <!-- <param name=\"loadStateFromDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_posegraph_solver/\" /> -->\n            <param name=\"loadStateFromDisk\" type=\"string\" value=\"\" />\n            <param name=\"saveStateToDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_posegraph_solver/\" />\n            <!-- <param name=\"saveStateToDisk\" type=\"string\" value=\"\" /> -->\n        </node>\n    </group>\n\n</launch>\n"
  },
  {
    "path": "launch/realsense_camera.launch",
    "content": "<launch>\n  <arg name=\"serial_no\"           default=\"\"/>\n  <arg name=\"json_file_path\"      default=\"\"/>\n  <arg name=\"camera\"              default=\"camera\"/>\n  <arg name=\"tf_prefix\"           default=\"$(arg camera)\"/>\n  <arg name=\"external_manager\"    default=\"false\"/>\n  <arg name=\"manager\"             default=\"realsense2_camera_manager\"/>\n\n  <arg name=\"fisheye_width\"       default=\"640\"/>\n  <arg name=\"fisheye_height\"      default=\"480\"/>\n  <arg name=\"enable_fisheye\"      default=\"false\"/>\n\n  <arg name=\"depth_width\"         default=\"640\"/>\n  <arg name=\"depth_height\"        default=\"480\"/>\n  <arg name=\"enable_depth\"        default=\"false\"/>\n\n  <arg name=\"infra_width\"        default=\"640\"/>\n  <arg name=\"infra_height\"       default=\"480\"/>\n  <arg name=\"enable_infra1\"       default=\"true\"/>\n  <arg name=\"enable_infra2\"       default=\"true\"/>\n\n  <arg name=\"color_width\"         default=\"640\"/>\n  <arg name=\"color_height\"        default=\"480\"/>\n  <arg name=\"enable_color\"        default=\"false\"/>\n\n  <arg name=\"fisheye_fps\"         default=\"30\"/>\n  <arg name=\"depth_fps\"           default=\"30\"/>\n  <arg name=\"infra_fps\"           default=\"30\"/>\n  <arg name=\"color_fps\"           default=\"30\"/>\n  <arg name=\"gyro_fps\"            default=\"200\"/>\n  <arg name=\"accel_fps\"           default=\"250\"/>\n  <arg name=\"enable_gyro\"         default=\"true\"/>\n  <arg name=\"enable_accel\"        default=\"true\"/>\n\n  <arg name=\"enable_pointcloud\"         default=\"false\"/>\n  <arg name=\"pointcloud_texture_stream\" default=\"RS2_STREAM_COLOR\"/>\n  <arg name=\"pointcloud_texture_index\"  default=\"0\"/>\n\n  <arg name=\"enable_sync\"               default=\"true\"/>\n  <arg name=\"align_depth\"               default=\"false\"/>\n\n  <arg name=\"filters\"                   default=\"\"/>\n  <arg name=\"clip_distance\"             default=\"-2\"/>\n  <arg name=\"linear_accel_cov\"          default=\"0.01\"/>\n  <arg name=\"initial_reset\"             default=\"false\"/>\n  <arg name=\"unite_imu_method\"          default=\"linear_interpolation\"/>\n  <arg name=\"hold_back_imu_for_frames\"          default=\"false\"/>\n  <arg name=\"topic_odom_in\"             default=\"odom_in\"/>\n  <arg name=\"calib_odom_file\"           default=\"\"/>\n  <arg name=\"publish_odom_tf\"           default=\"true\"/>\n  <arg name=\"allow_no_texture_points\"   default=\"false  \"/>\n  <rosparam>\n    /camera/stereo_module/emitter_enabled: false\n  </rosparam>\n\n  <group ns=\"$(arg camera)\">\n    <include file=\"$(find realsense2_camera)/launch/includes/nodelet.launch.xml\">\n      <arg name=\"tf_prefix\"                value=\"$(arg tf_prefix)\"/>\n      <!-- <arg name=\"external_manager\"         value=\"$(arg external_manager)\"/> -->\n      <arg name=\"manager\"                  value=\"$(arg manager)\"/>\n      <arg name=\"serial_no\"                value=\"$(arg serial_no)\"/>\n      <arg name=\"json_file_path\"           value=\"$(arg json_file_path)\"/>\n\n      <arg name=\"enable_pointcloud\"        value=\"$(arg enable_pointcloud)\"/>\n      <arg name=\"pointcloud_texture_stream\" value=\"$(arg pointcloud_texture_stream)\"/>\n      <arg name=\"pointcloud_texture_index\"  value=\"$(arg pointcloud_texture_index)\"/>\n      <arg name=\"enable_sync\"              value=\"$(arg enable_sync)\"/>\n      <arg name=\"align_depth\"              value=\"$(arg align_depth)\"/>\n\n      <arg name=\"fisheye_width\"            value=\"$(arg fisheye_width)\"/>\n      <arg name=\"fisheye_height\"           value=\"$(arg fisheye_height)\"/>\n      <arg name=\"enable_fisheye\"           value=\"$(arg enable_fisheye)\"/>\n\n      <arg name=\"depth_width\"              value=\"$(arg depth_width)\"/>\n      <arg name=\"depth_height\"             value=\"$(arg depth_height)\"/>\n      <arg name=\"enable_depth\"             value=\"$(arg enable_depth)\"/>\n\n      <arg name=\"color_width\"              value=\"$(arg color_width)\"/>\n      <arg name=\"color_height\"             value=\"$(arg color_height)\"/>\n      <arg name=\"enable_color\"             value=\"$(arg enable_color)\"/>\n\n      <arg name=\"infra_width\"              value=\"$(arg infra_width)\"/>\n      <arg name=\"infra_height\"             value=\"$(arg infra_height)\"/>\n      <arg name=\"enable_infra1\"            value=\"$(arg enable_infra1)\"/>\n      <arg name=\"enable_infra2\"            value=\"$(arg enable_infra2)\"/>\n\n      <arg name=\"fisheye_fps\"              value=\"$(arg fisheye_fps)\"/>\n      <arg name=\"depth_fps\"                value=\"$(arg depth_fps)\"/>\n      <arg name=\"infra_fps\"                value=\"$(arg infra_fps)\"/>\n      <arg name=\"color_fps\"                value=\"$(arg color_fps)\"/>\n      <arg name=\"gyro_fps\"                 value=\"$(arg gyro_fps)\"/>\n      <arg name=\"accel_fps\"                value=\"$(arg accel_fps)\"/>\n      <arg name=\"enable_gyro\"              value=\"$(arg enable_gyro)\"/>\n      <arg name=\"enable_accel\"             value=\"$(arg enable_accel)\"/>\n\n      <arg name=\"filters\"                  value=\"$(arg filters)\"/>\n      <arg name=\"clip_distance\"            value=\"$(arg clip_distance)\"/>\n      <arg name=\"linear_accel_cov\"         value=\"$(arg linear_accel_cov)\"/>\n      <arg name=\"initial_reset\"            value=\"$(arg initial_reset)\"/>\n      <arg name=\"unite_imu_method\"         value=\"$(arg unite_imu_method)\"/>\n      <arg name=\"topic_odom_in\"            value=\"$(arg topic_odom_in)\"/>\n      <arg name=\"calib_odom_file\"          value=\"$(arg calib_odom_file)\"/>\n      <arg name=\"publish_odom_tf\"          value=\"$(arg publish_odom_tf)\"/>\n      <arg name=\"allow_no_texture_points\"  value=\"$(arg allow_no_texture_points)\"/>\n    </include>\n  </group>\n</launch>\n"
  },
  {
    "path": "launch/realsense_vinsfusion.launch",
    "content": "<launch timeout=\"100.0\">\n\n    <!-- CONFIG FILE -->\n    <arg name=\"config_path\" default=\"$(find cerebro)/config/vinsfusion/djirosimu_realsense_d435i/realsense_stereo_imu_config.yaml\" />\n\n\n    <!-- BAG -->\n    <!-- <arg name=\"bag_path\" default=\"/Bulk_Data/ros_bags/realsense/\" /> -->\n\n\n    <!-- Teach repeat -->\n    <arg name=\"bag_file\" default=\"/teach-repeat-bags/2019-07-09-11-06-23.bag\" doc=\"teach\"/>\n    <!-- <arg name=\"bag_file\" default=\"/teach-repeat-bags/2019-07-09-11-08-10.bag\" doc=\"teach\"/> -->\n\n\n    <!-- <node pkg=\"rosbag\" type=\"play\" name=\"rosbag\" args=\"$(arg bag_path)/$(arg bag_file) -s 1 -d 2\" output=\"log\"/> -->\n\n    <!-- END Bag -->\n\n\n    <!-- VINS-Fusion Estimator -->\n    <!-- unlike previous vins-mono, even the feature tracker is put inside 1 node ie. vins_node.\n    $(ubuntu) rosrun vins vins_node /app/catkin_ws/src/cerebro/config/vinsfusion/mynteye/mynteye_stereo_imu_config.yaml\n    -->\n    <node name=\"vins_estimator\" pkg=\"vins\" type=\"vins_node\" args=\" $(arg config_path)\" output=\"screen\">\n    </node>\n\n    <group if=\"0\">\n        <!-- VINS-Fusion loop_fusion -->\n        <!-- This is the DBOW + posegraph optimization from Qin Tong\n        $(ubuntu) rosrun loop_fusion loop_fusion_node /app/catkin_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml\n        -->\n        <node name=\"loop_fusion\" pkg=\"loop_fusion\" type=\"loop_fusion_node\" args=\" $(arg config_path)\" output=\"log\">\n        </node>\n    </group>\n\n\n    <group if=\"0\">\n        <!-- Cerebro -->\n        <node name=\"cerebro_node\" pkg=\"cerebro\" type=\"cerebro_node\" output=\"log\">\n           <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n\n\n           <!-- <param name=\"loadStateFromDisk\" type=\"string\" value=\"\" />\n           <param name=\"saveStateToDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_cerebro/\" /> -->\n\n\n           <param name=\"loadStateFromDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_cerebro/\" />\n           <param name=\"saveStateToDisk\" type=\"string\" value=\"\" />\n\n        </node>\n\n        <!-- KERAS SERVER -->\n        <!-- $(ubuntu) rosrun cerebro whole_image_desc_compute_server.py _config_file:=/app/catk_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml -->\n        <node name=\"my_desc_server\" pkg=\"cerebro\" type=\"whole_image_desc_compute_server.py\" output=\"log\">\n          <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/mobilenet_conv7_allpairloss.keras\" /> -->\n          <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/modelarch_and_weights.h5\" /> -->\n          <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x3__mobilenet-conv_pw_6_relu__K16__allpairloss/modelarch_and_weights.700.h5\" /> -->\n          <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x3__mobilenetv2-block_9_add__K16__allpairloss/modelarch_and_weights.800.h5\" /> -->\n          <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x1__mobilenetv2-block_9_add__K16__allpairloss/modelarch_and_weights.2000.h5\" />\n\n\n           <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n           <!--         OR -->\n           <!--\n           <param name=\"nrows\" type=\"string\" value=\"480\" />\n           <param name=\"ncols\" type=\"string\" value=\"752\" />\n            -->\n\n           <param name=\"nchnls\" type=\"string\" value=\"1\" />\n           <!-- <param name=\"nchnls\" type=\"string\" value=\"3\" /> -->\n        </node>\n\n        <!-- Pose graph (kidnap aware) -->\n        <!-- $(ubuntu) rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam -->\n        <node name=\"keyframe_pose_graph_slam_node\" pkg=\"solve_keyframe_pose_graph\" type=\"keyframe_pose_graph_slam\" output=\"screen\" >\n\n            <!-- <param name=\"loadStateFromDisk\" type=\"string\" value=\"\" />\n            <param name=\"saveStateToDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_posegraph_solver/\" /> -->\n\n\n            <param name=\"loadStateFromDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_posegraph_solver/\" />\n            <param name=\"saveStateToDisk\" type=\"string\" value=\"\" />\n\n        </node>\n    </group>\n\n</launch>\n"
  },
  {
    "path": "launch/realsense_vinsfusion_ondrone_repeat.launch",
    "content": "<launch timeout=\"100.0\">\n\n    <!-- CONFIG FILE -->\n    <arg name=\"config_path\" default=\"$(find cerebro)/config/vinsfusion/realsense_d435i/realsense_stereo_imu_config.yaml\" />\n\n\n    <!-- BAG -->\n    <arg name=\"bag_path\" default=\"/Bulk_Data/ros_bags/realsense/\" />\n\n    <!-- Teach repeat -->\n    <arg name=\"bag_file\" default=\"/teach-repeat-bags/2019-07-09-11-06-23.bag\" doc=\"teach\"/>\n    <!-- <arg name=\"bag_file\" default=\"/teach-repeat-bags/2019-07-09-11-08-10.bag\" doc=\"teach\"/> -->\n\n\n    <!-- <node pkg=\"rosbag\" type=\"play\" name=\"rosbag\" args=\"$(arg bag_path)/$(arg bag_file) -s 1 -d 2\" output=\"log\"/> -->\n\n    <!-- END Bag -->\n\n\n    <!-- VINS-Fusion Estimator -->\n    <!-- unlike previous vins-mono, even the feature tracker is put inside 1 node ie. vins_node.\n    $(ubuntu) rosrun vins vins_node /app/catkin_ws/src/cerebro/config/vinsfusion/mynteye/mynteye_stereo_imu_config.yaml\n    -->\n    <node name=\"vins_estimator\" pkg=\"vins\" type=\"vins_node\" args=\" $(arg config_path)\" output=\"log\">\n    </node>\n\n    <group if=\"0\">\n        <!-- VINS-Fusion loop_fusion -->\n        <!-- This is the DBOW + posegraph optimization from Qin Tong\n        $(ubuntu) rosrun loop_fusion loop_fusion_node /app/catkin_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml\n        -->\n        <node name=\"loop_fusion\" pkg=\"loop_fusion\" type=\"loop_fusion_node\" args=\" $(arg config_path)\" output=\"log\">\n        </node>\n    </group>\n\n\n    <group if=\"1\">\n        <!-- Cerebro -->\n        <node name=\"cerebro_node\" pkg=\"cerebro\" type=\"cerebro_node\" output=\"log\">\n           <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n\n           <param name=\"loadStateFromDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_cerebro/\" />\n           <param name=\"saveStateToDisk\" type=\"string\" value=\"\" />\n\n        </node>\n\n        <!-- no need to run the default server as the server is run else using the package https://github.com/mpkuse/tx2_whole_image_desc_server -->\n        <group if=\"1\" >\n            <!-- KERAS SERVER -->\n            <!-- $(ubuntu) rosrun cerebro whole_image_desc_compute_server.py _config_file:=/app/catk_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml -->\n            <node name=\"my_desc_server\" pkg=\"cerebro\" type=\"whole_image_desc_compute_server.py\" output=\"log\">\n              <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/mobilenet_conv7_allpairloss.keras\" /> -->\n              <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/modelarch_and_weights.h5\" /> -->\n              <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x3__mobilenet-conv_pw_6_relu__K16__allpairloss/modelarch_and_weights.700.h5\" /> -->\n              <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x3__mobilenetv2-block_9_add__K16__allpairloss/modelarch_and_weights.800.h5\" /> -->\n              <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x1__mobilenetv2-block_9_add__K16__allpairloss/modelarch_and_weights.2000.h5\" />\n\n\n               <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n               <!--         OR -->\n               <!--\n               <param name=\"nrows\" type=\"string\" value=\"480\" />\n               <param name=\"ncols\" type=\"string\" value=\"752\" />\n                -->\n\n               <param name=\"nchnls\" type=\"string\" value=\"1\" />\n               <!-- <param name=\"nchnls\" type=\"string\" value=\"3\" /> -->\n            </node>\n        </group>\n\n        <!-- Pose graph (kidnap aware) -->\n        <!-- $(ubuntu) rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam -->\n        <node name=\"keyframe_pose_graph_slam_node\" pkg=\"solve_keyframe_pose_graph\" type=\"keyframe_pose_graph_slam\" output=\"screen\" >\n\n            <param name=\"loadStateFromDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_posegraph_solver/\" />\n            <param name=\"saveStateToDisk\" type=\"string\" value=\"\" />\n\n            <param name=\"adhoc_pubpath\" type=\"string\" value=\"true\" />\n            <param name=\"adhoc_pubw0_T_w1\" type=\"string\" value=\"false\" />\n\n\n        </node>\n    </group>\n\n</launch>\n"
  },
  {
    "path": "launch/realsense_vinsfusion_ondrone_teach.launch",
    "content": "<launch timeout=\"100.0\">\n\n    <!-- CONFIG FILE -->\n    <arg name=\"config_path\" default=\"$(find cerebro)/config/vinsfusion/realsense_d435i/realsense_stereo_imu_config.yaml\" />\n\n\n    <!-- BAG -->\n    <arg name=\"bag_path\" default=\"/Bulk_Data/ros_bags/realsense/\" />\n\n    <!-- Teach repeat -->\n    <arg name=\"bag_file\" default=\"/teach-repeat-bags/2019-07-09-11-06-23.bag\" doc=\"teach\"/>\n    <!-- <arg name=\"bag_file\" default=\"/teach-repeat-bags/2019-07-09-11-08-10.bag\" doc=\"teach\"/> -->\n\n\n    <!-- <node pkg=\"rosbag\" type=\"play\" name=\"rosbag\" args=\"$(arg bag_path)/$(arg bag_file) -s 1 -d 2\" output=\"log\"/> -->\n\n    <!-- END Bag -->\n\n\n    <!-- VINS-Fusion Estimator -->\n    <!-- unlike previous vins-mono, even the feature tracker is put inside 1 node ie. vins_node.\n    $(ubuntu) rosrun vins vins_node /app/catkin_ws/src/cerebro/config/vinsfusion/mynteye/mynteye_stereo_imu_config.yaml\n    -->\n    <node name=\"vins_estimator\" pkg=\"vins\" type=\"vins_node\" args=\" $(arg config_path)\" output=\"log\">\n    </node>\n\n    <group if=\"0\">\n        <!-- VINS-Fusion loop_fusion -->\n        <!-- This is the DBOW + posegraph optimization from Qin Tong\n        $(ubuntu) rosrun loop_fusion loop_fusion_node /app/catkin_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml\n        -->\n        <node name=\"loop_fusion\" pkg=\"loop_fusion\" type=\"loop_fusion_node\" args=\" $(arg config_path)\" output=\"log\">\n        </node>\n    </group>\n\n\n    <group if=\"1\">\n        <!-- Cerebro -->\n        <node name=\"cerebro_node\" pkg=\"cerebro\" type=\"cerebro_node\" output=\"screen\">\n           <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n\n           <param name=\"loadStateFromDisk\" type=\"string\" value=\"\" />\n           <param name=\"saveStateToDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_cerebro/\" />\n\n        </node>\n\n        <!-- no need to run the default server as the server is run else using the package https://github.com/mpkuse/tx2_whole_image_desc_server -->\n        <group if=\"1\" >\n            <!-- KERAS SERVER -->\n            <!-- $(ubuntu) rosrun cerebro whole_image_desc_compute_server.py _config_file:=/app/catk_ws/src/cerebro/config/vinsfusion/euroc/euroc_stereo_imu_config.yaml -->\n            <node name=\"my_desc_server\" pkg=\"cerebro\" type=\"whole_image_desc_compute_server.py\" output=\"log\">\n              <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/mobilenet_conv7_allpairloss.keras\" /> -->\n              <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/modelarch_and_weights.h5\" /> -->\n              <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x3__mobilenet-conv_pw_6_relu__K16__allpairloss/modelarch_and_weights.700.h5\" /> -->\n              <!-- <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x3__mobilenetv2-block_9_add__K16__allpairloss/modelarch_and_weights.800.h5\" /> -->\n              <param name=\"kerasmodel_file\" type=\"string\" value=\"$(find cerebro)/scripts/keras.models/June2019/centeredinput-m1to1-240x320x1__mobilenetv2-block_9_add__K16__allpairloss/modelarch_and_weights.2000.h5\" />\n\n\n               <param name=\"config_file\" type=\"string\" value=\"$(arg config_path)\" />\n               <!--         OR -->\n               <!--\n               <param name=\"nrows\" type=\"string\" value=\"480\" />\n               <param name=\"ncols\" type=\"string\" value=\"752\" />\n                -->\n\n               <param name=\"nchnls\" type=\"string\" value=\"1\" />\n               <!-- <param name=\"nchnls\" type=\"string\" value=\"3\" /> -->\n            </node>\n        </group>\n\n        <!-- Pose graph (kidnap aware) -->\n        <!-- $(ubuntu) rosrun solve_keyframe_pose_graph keyframe_pose_graph_slam -->\n        <node name=\"keyframe_pose_graph_slam_node\" pkg=\"solve_keyframe_pose_graph\" type=\"keyframe_pose_graph_slam\" output=\"log\" >\n\n            <param name=\"loadStateFromDisk\" type=\"string\" value=\"\" />\n            <param name=\"saveStateToDisk\" type=\"string\" value=\"/Bulk_Data/chkpts_posegraph_solver/\" />\n\n            <param name=\"adhoc_pubpath\" type=\"string\" value=\"true\" />\n            <param name=\"adhoc_pubw0_T_w1\" type=\"string\" value=\"false\" />\n\n\n        </node>\n    </group>\n\n</launch>\n"
  },
  {
    "path": "msg/LoopEdge.msg",
    "content": "time timestamp0\ntime timestamp1\ngeometry_msgs/Pose pose_1T0 # pose of 0 as observed from 1. \nfloat32 weight\nstring description\n"
  },
  {
    "path": "package.xml",
    "content": "<?xml version=\"1.0\"?>\n<package>\n  <name>cerebro</name>\n  <version>0.0.1</version>\n  <description>The cerebro package. There is a multi-threaded node which stores all the incoming info from vins.\n      There is a service to compute image level descriptor. Geometry is also a separate service.</description>\n\n  <!-- One maintainer tag required, multiple allowed, one person per tag -->\n  <!-- Example:  -->\n  <!-- <maintainer email=\"jane.doe@example.com\">Jane Doe</maintainer> -->\n  <maintainer email=\"mpkuse@connect.ust.hk\">mpkuse</maintainer>\n\n\n  <!-- One license tag required, multiple allowed, one license per tag -->\n  <!-- Commonly used license strings: -->\n  <!--   BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->\n  <license>TODO</license>\n\n\n  <!-- Url tags are optional, but mutiple are allowed, one per tag -->\n  <!-- Optional attribute type can be: website, bugtracker, or repository -->\n  <!-- Example: -->\n  <!-- <url type=\"website\">http://wiki.ros.org/cerebro</url> -->\n\n\n  <!-- Author tags are optional, mutiple are allowed, one per tag -->\n  <!-- Authors do not have to be maintianers, but could be -->\n  <!-- Example: -->\n  <!-- <author email=\"jane.doe@example.com\">Jane Doe</author> -->\n\n\n  <!-- The *_depend tags are used to specify dependencies -->\n  <!-- Dependencies can be catkin packages or system dependencies -->\n  <!-- Examples: -->\n  <!-- Use build_depend for packages you need at compile time: -->\n  <!--   <build_depend>message_generation</build_depend> -->\n  <!-- Use buildtool_depend for build tool packages: -->\n  <!--   <buildtool_depend>catkin</buildtool_depend> -->\n  <!-- Use run_depend for packages you need at runtime: -->\n  <!--   <run_depend>message_runtime</run_depend> -->\n  <!-- Use test_depend for packages you need only for testing: -->\n  <!--   <test_depend>gtest</test_depend> -->\n  <buildtool_depend>catkin</buildtool_depend>\n  <build_depend>cv_bridge</build_depend>\n  <build_depend>image_transport</build_depend>\n  <build_depend>roscpp</build_depend>\n  <build_depend>rospy</build_depend>\n  <build_depend>std_msgs</build_depend>\n  <build_depend>message_generation</build_depend>\n\n  <run_depend>cv_bridge</run_depend>\n  <run_depend>image_transport</run_depend>\n  <run_depend>roscpp</run_depend>\n  <run_depend>rospy</run_depend>\n  <run_depend>std_msgs</run_depend>\n  <run_depend>message_runtime</run_depend>\n\n\n  <!-- The export tag contains other, unspecified, tags -->\n  <export>\n    <!-- Other tools can request additional information be placed here -->\n\n  </export>\n</package>\n"
  },
  {
    "path": "rviz/dev-config.rviz",
    "content": "Panels:\n  - Class: rviz/Displays\n    Help Height: 78\n    Name: Displays\n    Property Tree Widget:\n      Expanded:\n        - /Global Options1\n        - /Status1\n        - /Ref1\n        - /Ref1/Grid1\n        - /Feat Tracker1/Input Image1\n        - /Feat Tracker1/Input Image1/Status1\n        - /VIO1\n        - /VIO1/Odom In Cam-frame-of-ref1/Cam Odometry1\n        - /Cerebro1\n        - /Cerebro1/Marker1\n        - /Cerebro1/Marker1/Namespaces1\n        - /AR_demo1\n        - /AR_demo1/AR_image_odm1/Status1\n        - /Solve Pose Graph Opt1\n        - /Solve Pose Graph Opt1/Marker1\n        - /Solve Pose Graph Opt1/Marker1/Status1\n        - /Solve Pose Graph Opt1/Marker1/Namespaces1\n        - /Solve Pose Graph Opt1/Debug Node Marker1/Namespaces1\n      Splitter Ratio: 0.62647754\n    Tree Height: 697\n  - Class: rviz/Selection\n    Name: Selection\n  - Class: rviz/Tool Properties\n    Expanded:\n      - /2D Pose Estimate1\n      - /2D Nav Goal1\n      - /Publish Point1\n    Name: Tool Properties\n    Splitter Ratio: 0.588679016\n  - Class: rviz/Time\n    Experimental: false\n    Name: Time\n    SyncMode: 0\n    SyncSource: Tracked Features\n  - Class: rviz/Displays\n    Help Height: 78\n    Name: Displays\n    Property Tree Widget:\n      Expanded: ~\n      Splitter Ratio: 0.5\n    Tree Height: 141\nVisualization Manager:\n  Class: \"\"\n  Displays:\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Axes\n          Enabled: true\n          Length: 1\n          Name: Axes\n          Radius: 0.200000003\n          Reference Frame: <Fixed Frame>\n          Value: true\n        - Alpha: 0.5\n          Cell Size: 1\n          Class: rviz/Grid\n          Color: 160; 160; 164\n          Enabled: true\n          Line Style:\n            Line Width: 0.0299999993\n            Value: Lines\n          Name: Grid\n          Normal Cell Count: 0\n          Offset:\n            X: 0\n            Y: 0\n            Z: 0\n          Plane: XY\n          Plane Cell Count: 20\n          Reference Frame: <Fixed Frame>\n          Value: true\n      Enabled: true\n      Name: Ref\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Image\n          Enabled: false\n          Image Topic: /cam0/image_raw\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: Input Image\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: false\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /feature_tracker/feature_img\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: Tracked Features\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n      Enabled: true\n      Name: Feat Tracker\n    - Class: rviz/Group\n      Displays:\n        - Alpha: 1\n          Buffer Length: 1\n          Class: rviz/Path\n          Color: 255; 85; 255\n          Enabled: true\n          Head Diameter: 0.300000012\n          Head Length: 0.200000003\n          Length: 0.300000012\n          Line Style: Lines\n          Line Width: 0.0299999993\n          Name: VIO Path\n          Offset:\n            X: 0\n            Y: 0\n            Z: 0\n          Pose Style: None\n          Radius: 0.0299999993\n          Shaft Diameter: 0.100000001\n          Shaft Length: 0.100000001\n          Topic: /vins_estimator/path\n          Unreliable: false\n          Value: true\n        - Class: rviz/MarkerArray\n          Enabled: true\n          Marker Topic: /vins_estimator/camera_pose_visual\n          Name: Visual\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: true\n        - Alpha: 1\n          Autocompute Intensity Bounds: true\n          Autocompute Value Bounds:\n            Max Value: 1.46719348\n            Min Value: -0.613372922\n            Value: true\n          Axis: Z\n          Channel Name: intensity\n          Class: rviz/PointCloud\n          Color: 0; 255; 0\n          Color Transformer: FlatColor\n          Decay Time: 100\n          Enabled: false\n          Invert Rainbow: false\n          Max Color: 255; 255; 255\n          Max Intensity: 4096\n          Min Color: 0; 0; 0\n          Min Intensity: 0\n          Name: KF PointCloud\n          Position Transformer: XYZ\n          Queue Size: 100\n          Selectable: true\n          Size (Pixels): 3\n          Size (m): 0.00999999978\n          Style: Points\n          Topic: /vins_estimator/keyframe_point\n          Unreliable: false\n          Use Fixed Frame: true\n          Use rainbow: true\n          Value: false\n        - Alpha: 1\n          Autocompute Intensity Bounds: true\n          Autocompute Value Bounds:\n            Max Value: 10\n            Min Value: -10\n            Value: true\n          Axis: Z\n          Channel Name: intensity\n          Class: rviz/PointCloud\n          Color: 255; 255; 255\n          Color Transformer: Intensity\n          Decay Time: 100\n          Enabled: false\n          Invert Rainbow: false\n          Max Color: 255; 255; 255\n          Max Intensity: 4096\n          Min Color: 0; 0; 0\n          Min Intensity: 0\n          Name: PointCloud\n          Position Transformer: XYZ\n          Queue Size: 10\n          Selectable: true\n          Size (Pixels): 3\n          Size (m): 0.00999999978\n          Style: Points\n          Topic: /vins_estimator/point_cloud\n          Unreliable: false\n          Use Fixed Frame: true\n          Use rainbow: true\n          Value: false\n        - Angle Tolerance: 0.100000001\n          Class: rviz/Odometry\n          Color: 170; 85; 255\n          Enabled: false\n          Keep: 100\n          Length: 1\n          Name: imu_T_cam\n          Position Tolerance: 0.100000001\n          Topic: /vins_estimator/extrinsic\n          Value: false\n        - Class: rviz/Group\n          Displays:\n            - Angle Tolerance: 0.100000001\n              Class: rviz/Odometry\n              Color: 255; 25; 0\n              Enabled: true\n              Keep: 100\n              Length: 1\n              Name: Cam Odometry\n              Position Tolerance: 0.100000001\n              Topic: /vins_estimator/camera_pose\n              Value: true\n          Enabled: false\n          Name: Odom In Cam-frame-of-ref\n        - Class: rviz/Group\n          Displays:\n            - Angle Tolerance: 0.100000001\n              Class: rviz/Odometry\n              Color: 0; 255; 0\n              Enabled: true\n              Keep: 100\n              Length: 1\n              Name: KF Odometry\n              Position Tolerance: 0.100000001\n              Topic: /vins_estimator/keyframe_pose\n              Value: true\n            - Angle Tolerance: 0.100000001\n              Class: rviz/Odometry\n              Color: 0; 170; 255\n              Enabled: true\n              Keep: 100\n              Length: 1\n              Name: IMU Odometry\n              Position Tolerance: 0.100000001\n              Topic: /vins_estimator/odometry\n              Value: true\n            - Angle Tolerance: 0.100000001\n              Class: rviz/Odometry\n              Color: 255; 25; 0\n              Enabled: true\n              Keep: 100\n              Length: 1\n              Name: IMU Prop 200hz\n              Position Tolerance: 0.100000001\n              Topic: /vins_estimator/imu_propagate\n              Value: true\n          Enabled: false\n          Name: Odom In IMU-Frame-of-ref\n        - Class: rviz/Group\n          Displays:\n            - Angle Tolerance: 0.100000001\n              Class: rviz/Odometry\n              Color: 255; 25; 0\n              Enabled: false\n              Keep: 100\n              Length: 1\n              Name: vinsmono-reloc-odom\n              Position Tolerance: 0.100000001\n              Topic: /vins_estimator/relo_relative_pose\n              Value: false\n            - Alpha: 1\n              Buffer Length: 1\n              Class: rviz/Path\n              Color: 255; 85; 255\n              Enabled: true\n              Head Diameter: 0.300000012\n              Head Length: 0.200000003\n              Length: 0.300000012\n              Line Style: Lines\n              Line Width: 0.0299999993\n              Name: vinsmono-relocalized_path\n              Offset:\n                X: 0\n                Y: 0\n                Z: 0\n              Pose Style: None\n              Radius: 0.0299999993\n              Shaft Diameter: 0.100000001\n              Shaft Length: 0.100000001\n              Topic: /vins_estimator/relocalization_path\n              Unreliable: false\n              Value: true\n            - Class: rviz/Image\n              Enabled: true\n              Image Topic: /pose_graph/match_image\n              Max Value: 1\n              Median window: 5\n              Min Value: 0\n              Name: vins-monomatch-images\n              Normalize Range: true\n              Queue Size: 2\n              Transport Hint: raw\n              Unreliable: false\n              Value: true\n            - Class: rviz/MarkerArray\n              Enabled: true\n              Marker Topic: /pose_graph/pose_graph\n              Name: vinsmono loop viz\n              Namespaces:\n                {}\n              Queue Size: 100\n              Value: true\n          Enabled: false\n          Name: vins-mono relocalized\n      Enabled: false\n      Name: VIO\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Marker\n          Enabled: false\n          Marker Topic: /cerebro_node/viz/framedata\n          Name: Marker\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: false\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /cerebro_node/viz/imagepaire\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: imagepaire\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n      Enabled: true\n      Name: Cerebro\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /ar_demo_node3_corrected/AR_image_corrected\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: AR_image_corrected\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /ar_demo_node3_odom/AR_image_odom\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: AR_image_odm\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n        - Class: rviz/MarkerArray\n          Enabled: false\n          Marker Topic: /points_and_lines/AR_object\n          Name: AR_object\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: false\n        - Class: rviz/InteractiveMarkers\n          Enable Transparency: true\n          Enabled: true\n          Name: InteractiveMarkers\n          Show Axes: false\n          Show Descriptions: true\n          Show Visual Aids: false\n          Update Topic: /interactive_marker_server/update\n          Value: true\n      Enabled: true\n      Name: AR_demo\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Marker\n          Enabled: true\n          Marker Topic: /keyframe_pose_graph_slam_node/viz/visualization_marker\n          Name: Marker\n          Namespaces:\n            latest_odometry: true\n            world##_cam_visual: true\n            world#-1: true\n            world#-2: true\n            world#-3: true\n            world#-4: true\n            world#0: true\n            world#1: true\n            world#2: true\n            world#3: true\n            world#4: true\n          Queue Size: 100\n          Value: true\n        - Class: rviz/Marker\n          Enabled: false\n          Marker Topic: /debug_pose_graph_solver/visualization_marker\n          Name: Debug Node Marker\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: false\n      Enabled: true\n      Name: Solve Pose Graph Opt\n  Enabled: true\n  Global Options:\n    Background Color: 48; 48; 48\n    Fixed Frame: world\n    Frame Rate: 30\n  Name: root\n  Tools:\n    - Class: rviz/Interact\n      Hide Inactive Objects: true\n    - Class: rviz/MoveCamera\n    - Class: rviz/Select\n    - Class: rviz/FocusCamera\n    - Class: rviz/Measure\n    - Class: rviz/SetInitialPose\n      Topic: /initialpose\n    - Class: rviz/SetGoal\n      Topic: /move_base_simple/goal\n    - Class: rviz/PublishPoint\n      Single click: true\n      Topic: /clicked_point\n  Value: true\n  Views:\n    Current:\n      Class: rviz/Orbit\n      Distance: 46.7297897\n      Enable Stereo Rendering:\n        Stereo Eye Separation: 0.0599999987\n        Stereo Focal Distance: 1\n        Swap Stereo Eyes: false\n        Value: false\n      Focal Point:\n        X: 3.76500964\n        Y: 2.58213377\n        Z: -2.90765786\n      Focal Shape Fixed Size: false\n      Focal Shape Size: 0.0500000007\n      Name: Current View\n      Near Clip Distance: 0.00999999978\n      Pitch: 1.0647974\n      Target Frame: <Fixed Frame>\n      Value: Orbit (rviz)\n      Yaw: 4.69420815\n    Saved:\n      - Class: rviz/Orbit\n        Distance: 49.6446877\n        Enable Stereo Rendering:\n          Stereo Eye Separation: 0.0599999987\n          Stereo Focal Distance: 1\n          Swap Stereo Eyes: false\n          Value: false\n        Focal Point:\n          X: 22.8474731\n          Y: 20.4583664\n          Z: -6.84013748\n        Focal Shape Fixed Size: true\n        Focal Shape Size: 0.0500000007\n        Name: Orbit\n        Near Clip Distance: 0.00999999978\n        Pitch: 1.16479671\n        Target Frame: <Fixed Frame>\n        Value: Orbit (rviz)\n        Yaw: 5.02046442\nWindow Geometry:\n  AR_image_corrected:\n    collapsed: false\n  AR_image_odm:\n    collapsed: false\n  Displays:\n    collapsed: false\n  Height: 1003\n  Hide Left Dock: false\n  Hide Right Dock: false\n  Input Image:\n    collapsed: false\n  QMainWindow State: 000000ff00000000fd00000004000000000000016a00000348fc0200000017fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000006400fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afc00000041000001e00000000000fffffffa000000000100000001fb0000000a0049006d0061006700650000000000ffffffff0000000000000000fb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006501000000410000005f0000000000000000fb0000000a0049006d00610067006501000002c4000000c60000000000000000fb0000001a0043006c0075007300740065007200200041007300730067006e0100000357000000160000000000000000fb0000002c0050006f007300650020004700720061007000680020004d006100740063006800200049006d0061006700650100000373000000160000000000000000fb0000001e0043006f006c006f0063006100740069006f006e002000500061006900720000000000ffffffff0000000000000000fb0000000c004600720061006d006500730100000041000000420000000000000000fb000000160049006e00700075007400200049006d0061006700650000000041000001670000001600fffffffb000000200054007200610063006b006500640020004600650061007400750072006500730100000041000000a60000001600fffffffb0000000a0049006d00610067006501000002c3000000c60000000000000000fb0000001800410052005f0069006d006100670065005f006f0064006d01000000ed000001110000001600fffffffb0000001a004c006f006f007000430061006e0064006900640061007400650100000373000000160000000000000000fb000000140069006d0061006700650070006100690072006501000002040000009d0000001600fffffffb0000002400410052005f0069006d006100670065005f0063006f007200720065006300740065006401000002a7000000e20000001600fffffffb0000002a00760069006e0073002d006d006f006e006f006d0061007400630068002d0069006d006100670065007300000002c4000000c50000001600fffffffb000000100044006900730070006c006100790073020000021a000002b40000016c0000011e00000001000001b900000348fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fc0000004100000348000000dd00fffffffa000000000100000002fb000000100044006900730070006c0061007900730100000000ffffffff0000016a00fffffffb0000000a00560069006500770073010000066f0000010f0000000000000000fb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000077e0000003efc0100000002fb0000000800540069006d006501000000000000077e0000030000fffffffb0000000800540069006d006501000000000000045000000000000000000000044f0000034800000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000\n  Selection:\n    collapsed: false\n  Time:\n    collapsed: false\n  Tool Properties:\n    collapsed: false\n  Tracked Features:\n    collapsed: false\n  Width: 1918\n  X: 0\n  Y: 24\n  imagepaire:\n    collapsed: false\n  vins-monomatch-images:\n    collapsed: false\n"
  },
  {
    "path": "rviz/good-viz.rviz",
    "content": "Panels:\n  - Class: rviz/Displays\n    Help Height: 78\n    Name: Displays\n    Property Tree Widget:\n      Expanded:\n        - /Global Options1\n        - /Status1\n        - /Ref1\n        - /Ref1/Axes1\n        - /Feat Tracker1/Input Image1\n        - /Feat Tracker1/Input Image1/Status1\n        - /VIO1\n        - /VIO1/Odom In Cam-frame-of-ref1/Cam Odometry1\n        - /Cerebro1\n        - /Cerebro1/Marker1\n        - /Cerebro1/Marker1/Namespaces1\n        - /AR_demo1\n        - /Solve Pose Graph Opt1\n        - /Solve Pose Graph Opt1/Marker1\n        - /Solve Pose Graph Opt1/Marker1/Namespaces1\n        - /Solve Pose Graph Opt1/Debug Node Marker1/Namespaces1\n        - /Ref21\n        - /Ref21/Grid1/Offset1\n      Splitter Ratio: 0.62647754\n    Tree Height: 805\n  - Class: rviz/Selection\n    Name: Selection\n  - Class: rviz/Tool Properties\n    Expanded:\n      - /2D Pose Estimate1\n      - /2D Nav Goal1\n      - /Publish Point1\n    Name: Tool Properties\n    Splitter Ratio: 0.588679016\n  - Class: rviz/Time\n    Experimental: false\n    Name: Time\n    SyncMode: 0\n    SyncSource: Tracked Features\n  - Class: rviz/Displays\n    Help Height: 78\n    Name: Displays\n    Property Tree Widget:\n      Expanded: ~\n      Splitter Ratio: 0.5\n    Tree Height: 359\nVisualization Manager:\n  Class: \"\"\n  Displays:\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Axes\n          Enabled: true\n          Length: 1\n          Name: Axes\n          Radius: 0.200000003\n          Reference Frame: <Fixed Frame>\n          Value: true\n        - Alpha: 0.5\n          Cell Size: 1\n          Class: rviz/Grid\n          Color: 160; 160; 164\n          Enabled: true\n          Line Style:\n            Line Width: 0.0299999993\n            Value: Lines\n          Name: Grid\n          Normal Cell Count: 0\n          Offset:\n            X: 0\n            Y: 0\n            Z: 0\n          Plane: XY\n          Plane Cell Count: 20\n          Reference Frame: <Fixed Frame>\n          Value: true\n      Enabled: true\n      Name: Ref\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Image\n          Enabled: false\n          Image Topic: /cam0/image_raw\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: Input Image\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: false\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /feature_tracker/feature_img\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: Tracked Features\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n      Enabled: true\n      Name: Feat Tracker\n    - Class: rviz/Group\n      Displays:\n        - Alpha: 1\n          Buffer Length: 1\n          Class: rviz/Path\n          Color: 255; 85; 255\n          Enabled: true\n          Head Diameter: 0.300000012\n          Head Length: 0.200000003\n          Length: 0.300000012\n          Line Style: Lines\n          Line Width: 0.0299999993\n          Name: VIO Path\n          Offset:\n            X: 0\n            Y: 0\n            Z: 0\n          Pose Style: None\n          Radius: 0.0299999993\n          Shaft Diameter: 0.100000001\n          Shaft Length: 0.100000001\n          Topic: /vins_estimator/path\n          Unreliable: false\n          Value: true\n        - Class: rviz/MarkerArray\n          Enabled: true\n          Marker Topic: /vins_estimator/camera_pose_visual\n          Name: Visual\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: true\n        - Alpha: 1\n          Autocompute Intensity Bounds: true\n          Autocompute Value Bounds:\n            Max Value: 1.46719348\n            Min Value: -0.613372922\n            Value: true\n          Axis: Z\n          Channel Name: intensity\n          Class: rviz/PointCloud\n          Color: 0; 255; 0\n          Color Transformer: FlatColor\n          Decay Time: 100\n          Enabled: false\n          Invert Rainbow: false\n          Max Color: 255; 255; 255\n          Max Intensity: 4096\n          Min Color: 0; 0; 0\n          Min Intensity: 0\n          Name: KF PointCloud\n          Position Transformer: XYZ\n          Queue Size: 100\n          Selectable: true\n          Size (Pixels): 3\n          Size (m): 0.00999999978\n          Style: Points\n          Topic: /vins_estimator/keyframe_point\n          Unreliable: false\n          Use Fixed Frame: true\n          Use rainbow: true\n          Value: false\n        - Alpha: 1\n          Autocompute Intensity Bounds: true\n          Autocompute Value Bounds:\n            Max Value: 10\n            Min Value: -10\n            Value: true\n          Axis: Z\n          Channel Name: intensity\n          Class: rviz/PointCloud\n          Color: 255; 255; 255\n          Color Transformer: Intensity\n          Decay Time: 100\n          Enabled: false\n          Invert Rainbow: false\n          Max Color: 255; 255; 255\n          Max Intensity: 4096\n          Min Color: 0; 0; 0\n          Min Intensity: 0\n          Name: PointCloud\n          Position Transformer: XYZ\n          Queue Size: 10\n          Selectable: true\n          Size (Pixels): 3\n          Size (m): 0.00999999978\n          Style: Points\n          Topic: /vins_estimator/point_cloud\n          Unreliable: false\n          Use Fixed Frame: true\n          Use rainbow: true\n          Value: false\n        - Angle Tolerance: 0.100000001\n          Class: rviz/Odometry\n          Color: 170; 85; 255\n          Enabled: false\n          Keep: 100\n          Length: 1\n          Name: imu_T_cam\n          Position Tolerance: 0.100000001\n          Topic: /vins_estimator/extrinsic\n          Value: false\n        - Class: rviz/Group\n          Displays:\n            - Angle Tolerance: 0.100000001\n              Class: rviz/Odometry\n              Color: 255; 25; 0\n              Enabled: true\n              Keep: 100\n              Length: 1\n              Name: Cam Odometry\n              Position Tolerance: 0.100000001\n              Topic: /vins_estimator/camera_pose\n              Value: true\n          Enabled: false\n          Name: Odom In Cam-frame-of-ref\n        - Class: rviz/Group\n          Displays:\n            - Angle Tolerance: 0.100000001\n              Class: rviz/Odometry\n              Color: 0; 255; 0\n              Enabled: true\n              Keep: 100\n              Length: 1\n              Name: KF Odometry\n              Position Tolerance: 0.100000001\n              Topic: /vins_estimator/keyframe_pose\n              Value: true\n            - Angle Tolerance: 0.100000001\n              Class: rviz/Odometry\n              Color: 0; 170; 255\n              Enabled: true\n              Keep: 100\n              Length: 1\n              Name: IMU Odometry\n              Position Tolerance: 0.100000001\n              Topic: /vins_estimator/odometry\n              Value: true\n            - Angle Tolerance: 0.100000001\n              Class: rviz/Odometry\n              Color: 255; 25; 0\n              Enabled: true\n              Keep: 100\n              Length: 1\n              Name: IMU Prop 200hz\n              Position Tolerance: 0.100000001\n              Topic: /vins_estimator/imu_propagate\n              Value: true\n          Enabled: false\n          Name: Odom In IMU-Frame-of-ref\n        - Class: rviz/Group\n          Displays:\n            - Angle Tolerance: 0.100000001\n              Class: rviz/Odometry\n              Color: 255; 25; 0\n              Enabled: false\n              Keep: 100\n              Length: 1\n              Name: vinsmono-reloc-odom\n              Position Tolerance: 0.100000001\n              Topic: /vins_estimator/relo_relative_pose\n              Value: false\n            - Alpha: 1\n              Buffer Length: 1\n              Class: rviz/Path\n              Color: 255; 85; 255\n              Enabled: true\n              Head Diameter: 0.300000012\n              Head Length: 0.200000003\n              Length: 0.300000012\n              Line Style: Lines\n              Line Width: 0.0299999993\n              Name: vinsmono-relocalized_path\n              Offset:\n                X: 0\n                Y: 0\n                Z: 0\n              Pose Style: None\n              Radius: 0.0299999993\n              Shaft Diameter: 0.100000001\n              Shaft Length: 0.100000001\n              Topic: /vins_estimator/relocalization_path\n              Unreliable: false\n              Value: true\n            - Class: rviz/Image\n              Enabled: true\n              Image Topic: /pose_graph/match_image\n              Max Value: 1\n              Median window: 5\n              Min Value: 0\n              Name: vins-monomatch-images\n              Normalize Range: true\n              Queue Size: 2\n              Transport Hint: raw\n              Unreliable: false\n              Value: true\n            - Class: rviz/MarkerArray\n              Enabled: true\n              Marker Topic: /pose_graph/pose_graph\n              Name: vinsmono loop viz\n              Namespaces:\n                {}\n              Queue Size: 100\n              Value: true\n          Enabled: false\n          Name: vins-mono relocalized\n      Enabled: false\n      Name: VIO\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Marker\n          Enabled: true\n          Marker Topic: /cerebro_node/viz/framedata\n          Name: Marker\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: true\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /cerebro_node/viz/imagepaire\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: imagepaire\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n      Enabled: true\n      Name: Cerebro\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /ar_demo_node3_corrected/AR_image_corrected\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: AR_image_corrected\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /ar_demo_node3_odom/AR_image_odom\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: AR_image_odm\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n        - Class: rviz/MarkerArray\n          Enabled: false\n          Marker Topic: /points_and_lines/AR_object\n          Name: AR_object\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: false\n        - Class: rviz/InteractiveMarkers\n          Enable Transparency: true\n          Enabled: true\n          Name: InteractiveMarkers\n          Show Axes: false\n          Show Descriptions: true\n          Show Visual Aids: false\n          Update Topic: /interactive_marker_server/update\n          Value: true\n      Enabled: false\n      Name: AR_demo\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Marker\n          Enabled: true\n          Marker Topic: /keyframe_pose_graph_slam_node/viz/visualization_marker\n          Name: Marker\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: true\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /keyframe_pose_graph_slam_node/viz/disjoint_set_status_image\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: disjoint_set_status_image\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n        - Class: rviz/Marker\n          Enabled: false\n          Marker Topic: /debug_pose_graph_solver/visualization_marker\n          Name: Debug Node Marker\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: false\n      Enabled: true\n      Name: Solve Pose Graph Opt\n    - Class: rviz/Group\n      Displays:\n        - Alpha: 0.5\n          Cell Size: 1\n          Class: rviz/Grid\n          Color: 40; 164; 44\n          Enabled: true\n          Line Style:\n            Line Width: 0.0299999993\n            Value: Lines\n          Name: Grid\n          Normal Cell Count: 0\n          Offset:\n            X: 30\n            Y: 0\n            Z: 0\n          Plane: XY\n          Plane Cell Count: 20\n          Reference Frame: <Fixed Frame>\n          Value: true\n      Enabled: true\n      Name: Ref2\n  Enabled: true\n  Global Options:\n    Background Color: 48; 48; 48\n    Fixed Frame: world\n    Frame Rate: 30\n  Name: root\n  Tools:\n    - Class: rviz/Interact\n      Hide Inactive Objects: true\n    - Class: rviz/MoveCamera\n    - Class: rviz/Select\n    - Class: rviz/FocusCamera\n    - Class: rviz/Measure\n    - Class: rviz/SetInitialPose\n      Topic: /initialpose\n    - Class: rviz/SetGoal\n      Topic: /move_base_simple/goal\n    - Class: rviz/PublishPoint\n      Single click: true\n      Topic: /clicked_point\n  Value: true\n  Views:\n    Current:\n      Class: rviz/Orbit\n      Distance: 49.2109833\n      Enable Stereo Rendering:\n        Stereo Eye Separation: 0.0599999987\n        Stereo Focal Distance: 1\n        Swap Stereo Eyes: false\n        Value: false\n      Focal Point:\n        X: 3.41268897\n        Y: -0.305300832\n        Z: -1.72543097\n      Focal Shape Fixed Size: false\n      Focal Shape Size: 0.0500000007\n      Name: Current View\n      Near Clip Distance: 0.00999999978\n      Pitch: 0.884797573\n      Target Frame: <Fixed Frame>\n      Value: Orbit (rviz)\n      Yaw: 3.05431199\n    Saved:\n      - Class: rviz/Orbit\n        Distance: 49.6446877\n        Enable Stereo Rendering:\n          Stereo Eye Separation: 0.0599999987\n          Stereo Focal Distance: 1\n          Swap Stereo Eyes: false\n          Value: false\n        Focal Point:\n          X: 22.8474731\n          Y: 20.4583664\n          Z: -6.84013748\n        Focal Shape Fixed Size: true\n        Focal Shape Size: 0.0500000007\n        Name: Orbit\n        Near Clip Distance: 0.00999999978\n        Pitch: 1.16479671\n        Target Frame: <Fixed Frame>\n        Value: Orbit (rviz)\n        Yaw: 5.02046442\nWindow Geometry:\n  AR_image_corrected:\n    collapsed: false\n  AR_image_odm:\n    collapsed: false\n  Displays:\n    collapsed: false\n  Height: 1003\n  Hide Left Dock: false\n  Hide Right Dock: false\n  Input Image:\n    collapsed: false\n  QMainWindow State: 000000ff00000000fd00000004000000000000016a000003b4fc0200000017fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000006400fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afc00000041000001e00000000000fffffffa000000000100000001fb0000000a0049006d0061006700650000000000ffffffff0000000000000000fb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d00610067006501000000410000005f0000000000000000fb0000000a0049006d00610067006501000002c4000000c60000000000000000fb0000001a0043006c0075007300740065007200200041007300730067006e0100000357000000160000000000000000fb0000002c0050006f007300650020004700720061007000680020004d006100740063006800200049006d0061006700650100000373000000160000000000000000fb0000001e0043006f006c006f0063006100740069006f006e002000500061006900720000000000ffffffff0000000000000000fb0000000c004600720061006d006500730100000041000000420000000000000000fb000000160049006e00700075007400200049006d0061006700650000000041000001670000001600fffffffb000000200054007200610063006b006500640020004600650061007400750072006500730100000019000001470000001600fffffffb0000000a0049006d00610067006501000002c3000000c60000000000000000fb0000001800410052005f0069006d006100670065005f006f0064006d000000011a0000002d0000001600fffffffb0000001a004c006f006f007000430061006e0064006900640061007400650100000373000000160000000000000000fb000000140069006d006100670065007000610069007200650100000166000001790000001600fffffffb0000002400410052005f0069006d006100670065005f0063006f00720072006500630074006500640000000202000000a00000001600fffffffb0000002a00760069006e0073002d006d006f006e006f006d0061007400630068002d0069006d006100670065007300000002c4000000c50000001600fffffffb00000032006400690073006a006f0069006e0074005f007300650074005f007300740061007400750073005f0069006d00610067006501000002e5000000e80000001600ffffff000000010000016a000003b4fc0200000004fb000000100044006900730070006c0061007900730100000019000003b4000000dd00fffffffb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fc00000041000003480000000000fffffffa000000010100000002fb000000100044006900730070006c0061007900730000000000ffffffff0000016a00fffffffb0000000a00560069006500770073010000066f0000010f0000000000000000fb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000077e0000003bfc0100000002fb0000000800540069006d006500000000000000077e0000030000fffffffb0000000800540069006d006501000000000000045000000000000000000000049e000003b400000004000000040000000800000008fc000000030000000000000000000000020000000000000003000000010000000a0054006f006f006c00730000000000ffffffff0000000000000000\n  Selection:\n    collapsed: false\n  Time:\n    collapsed: false\n  Tool Properties:\n    collapsed: false\n  Tracked Features:\n    collapsed: false\n  Width: 1918\n  X: 0\n  Y: 24\n  disjoint_set_status_image:\n    collapsed: false\n  imagepaire:\n    collapsed: false\n  vins-monomatch-images:\n    collapsed: false\n"
  },
  {
    "path": "rviz/vins-fusion.rviz",
    "content": "Panels:\n  - Class: rviz/Displays\n    Help Height: 78\n    Name: Displays\n    Property Tree Widget:\n      Expanded:\n        - /Global Options1\n        - /Status1\n        - /raw_input_image1\n        - /Ref1\n        - /Ref1/Axes1\n        - /Ref1/Grid1\n        - /Feat Tracker1/Input Image1\n        - /AR_demo1\n        - /AR_demo1/AR_image_corrected1\n        - /AR_demo1/AR_image_odm1\n        - /AR_demo1/ModelViz1\n        - /AR_demo1/ModelViz1/Status1\n        - /AR_demo1/ModelViz1/Namespaces1\n        - /Cerebro1\n        - /Cerebro1/Marker1\n        - /Cerebro1/Marker1/Namespaces1\n        - /Solve Pose Graph Opt1/Marker1\n        - /Solve Pose Graph Opt1/Marker1/Namespaces1\n        - /Solve Pose Graph Opt1/Debug Node Marker1/Namespaces1\n        - /VINS-fusion1/MarkerArray1/Namespaces1\n        - /VINS-fusion1/VIO Path1\n        - /VINS-fusion1/VIO Path1/Offset1\n        - /VINS-fusion1/After Loop Fusion1\n        - /VINS-fusion1/Loop Fusion Edges1\n        - /Marker1\n        - /Surfel Mapping1/PointCloud21\n      Splitter Ratio: 0.62647754\n    Tree Height: 853\n  - Class: rviz/Selection\n    Name: Selection\n  - Class: rviz/Tool Properties\n    Expanded:\n      - /2D Pose Estimate1\n      - /2D Nav Goal1\n      - /Publish Point1\n    Name: Tool Properties\n    Splitter Ratio: 0.588679016\n  - Class: rviz/Time\n    Experimental: false\n    Name: Time\n    SyncMode: 0\n    SyncSource: raw_input_image\n  - Class: rviz/Displays\n    Help Height: 78\n    Name: Displays\n    Property Tree Widget:\n      Expanded: ~\n      Splitter Ratio: 0.5\n    Tree Height: 359\n  - Class: rviz/Views\n    Expanded:\n      - /Current View1\n    Name: Views\n    Splitter Ratio: 0.5\nVisualization Manager:\n  Class: \"\"\n  Displays:\n    - Class: rviz/Image\n      Enabled: true\n      Image Topic: /camera/infra1/image_rect_raw\n      Max Value: 1\n      Median window: 5\n      Min Value: 0\n      Name: raw_input_image\n      Normalize Range: true\n      Queue Size: 2\n      Transport Hint: raw\n      Unreliable: false\n      Value: true\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Axes\n          Enabled: false\n          Length: 1\n          Name: Axes\n          Radius: 0.200000003\n          Reference Frame: <Fixed Frame>\n          Value: false\n        - Alpha: 0.5\n          Cell Size: 1\n          Class: rviz/Grid\n          Color: 160; 160; 164\n          Enabled: true\n          Line Style:\n            Line Width: 0.0299999993\n            Value: Lines\n          Name: Grid\n          Normal Cell Count: 0\n          Offset:\n            X: 0\n            Y: 0\n            Z: 0\n          Plane: XY\n          Plane Cell Count: 20\n          Reference Frame: <Fixed Frame>\n          Value: true\n      Enabled: true\n      Name: Ref\n    - Class: rviz/Group\n      Displays:\n        - Alpha: 0.5\n          Cell Size: 1\n          Class: rviz/Grid\n          Color: 40; 164; 44\n          Enabled: true\n          Line Style:\n            Line Width: 0.0299999993\n            Value: Lines\n          Name: Grid\n          Normal Cell Count: 0\n          Offset:\n            X: 30\n            Y: 0\n            Z: 0\n          Plane: XY\n          Plane Cell Count: 20\n          Reference Frame: <Fixed Frame>\n          Value: true\n      Enabled: true\n      Name: Ref2\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /cam0/image_raw\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: Input Image\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /feature_tracker/feature_img\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: Tracked Features\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n      Enabled: false\n      Name: Feat Tracker\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /ar_demo_node3_odom/AR_image_corrected\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: AR_image_corrected\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n        - Class: rviz/Image\n          Enabled: false\n          Image Topic: /ar_demo_node3_odom/AR_image_odom\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: AR_image_odm\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: false\n        - Class: rviz/MarkerArray\n          Enabled: false\n          Marker Topic: /points_and_lines/AR_object\n          Name: AR_object\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: false\n        - Alpha: 1\n          Autocompute Intensity Bounds: true\n          Autocompute Value Bounds:\n            Max Value: 10\n            Min Value: -10\n            Value: true\n          Axis: Z\n          Channel Name: x\n          Class: rviz/PointCloud2\n          Color: 255; 255; 255\n          Color Transformer: Intensity\n          Decay Time: 0\n          Enabled: true\n          Invert Rainbow: false\n          Max Color: 255; 255; 255\n          Max Intensity: 9.04478359\n          Min Color: 0; 0; 0\n          Min Intensity: 3.50075197\n          Name: debug ptcld\n          Position Transformer: XYZ\n          Queue Size: 10\n          Selectable: true\n          Size (Pixels): 3\n          Size (m): 0.0199999996\n          Style: Flat Squares\n          Topic: /unit_test_estimate_ground_plane/pt_cld\n          Unreliable: false\n          Use Fixed Frame: true\n          Use rainbow: true\n          Value: true\n        - Class: rviz/Marker\n          Enabled: true\n          Marker Topic: /ar_demo_node3_odom/models\n          Name: ModelViz\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: true\n        - Class: rviz/InteractiveMarkers\n          Enable Transparency: true\n          Enabled: true\n          Name: InteractiveMarkers\n          Show Axes: false\n          Show Descriptions: true\n          Show Visual Aids: false\n          Update Topic: /interactive_marker_server/update\n          Value: true\n      Enabled: true\n      Name: AR_demo\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Marker\n          Enabled: false\n          Marker Topic: /cerebro_node/viz/framedata\n          Name: Marker\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: false\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /cerebro_node/viz/imagepaire\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: cerebro - imagepaire\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n      Enabled: true\n      Name: Cerebro\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Marker\n          Enabled: true\n          Marker Topic: /keyframe_pose_graph_slam_node/viz/visualization_marker\n          Name: Marker\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: true\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /keyframe_pose_graph_slam_node/viz/disjoint_set_status_image\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: disjoint_set_status_image\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n        - Class: rviz/Marker\n          Enabled: false\n          Marker Topic: /debug_pose_graph_solver/visualization_marker\n          Name: Debug Node Marker\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: false\n      Enabled: true\n      Name: Solve Pose Graph Opt\n    - Class: rviz/Group\n      Displays:\n        - Class: rviz/Image\n          Enabled: true\n          Image Topic: /loop_fusion/match_image\n          Max Value: 1\n          Median window: 5\n          Min Value: 0\n          Name: DBOW Match Image\n          Normalize Range: true\n          Queue Size: 2\n          Transport Hint: raw\n          Unreliable: false\n          Value: true\n        - Class: rviz/MarkerArray\n          Enabled: true\n          Marker Topic: /vins_estimator/camera_pose_visual\n          Name: MarkerArray\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: true\n        - Alpha: 1\n          Buffer Length: 1\n          Class: rviz/Path\n          Color: 255; 85; 255\n          Enabled: true\n          Head Diameter: 0.300000012\n          Head Length: 0.200000003\n          Length: 0.300000012\n          Line Style: Lines\n          Line Width: 0.0299999993\n          Name: VIO Path\n          Offset:\n            X: 0\n            Y: 0\n            Z: 0\n          Pose Style: None\n          Radius: 0.0299999993\n          Shaft Diameter: 0.100000001\n          Shaft Length: 0.100000001\n          Topic: /vins_estimator/path\n          Unreliable: false\n          Value: true\n        - Alpha: 1\n          Buffer Length: 1\n          Class: rviz/Path\n          Color: 255; 85; 255\n          Enabled: true\n          Head Diameter: 0.300000012\n          Head Length: 0.200000003\n          Length: 0.300000012\n          Line Style: Lines\n          Line Width: 0.0299999993\n          Name: After Loop Fusion\n          Offset:\n            X: 0\n            Y: 0\n            Z: 0\n          Pose Style: None\n          Radius: 0.0299999993\n          Shaft Diameter: 0.100000001\n          Shaft Length: 0.100000001\n          Topic: /loop_fusion/pose_graph_path\n          Unreliable: false\n          Value: true\n        - Class: rviz/MarkerArray\n          Enabled: true\n          Marker Topic: /loop_fusion/pose_graph\n          Name: Loop Fusion Edges\n          Namespaces:\n            {}\n          Queue Size: 100\n          Value: true\n      Enabled: false\n      Name: VINS-fusion\n    - Class: rviz/Marker\n      Enabled: false\n      Marker Topic: /keyframe_pose_graph_slam_node/hz200/visualization_marker\n      Name: Marker\n      Namespaces:\n        {}\n      Queue Size: 100\n      Value: false\n    - Class: rviz/Group\n      Displays:\n        - Alpha: 1\n          Autocompute Intensity Bounds: true\n          Autocompute Value Bounds:\n            Max Value: 10\n            Min Value: -10\n            Value: true\n          Axis: Z\n          Channel Name: z\n          Class: rviz/PointCloud2\n          Color: 255; 255; 255\n          Color Transformer: Intensity\n          Decay Time: 0\n          Enabled: true\n          Invert Rainbow: false\n          Max Color: 255; 255; 255\n          Max Intensity: 0.863711178\n          Min Color: 0; 0; 0\n          Min Intensity: -2.19427705\n          Name: PointCloud2\n          Position Transformer: XYZ\n          Queue Size: 10\n          Selectable: true\n          Size (Pixels): 3\n          Size (m): 0.0500000007\n          Style: Flat Squares\n          Topic: /surfel_fusion/pointcloud\n          Unreliable: false\n          Use Fixed Frame: true\n          Use rainbow: true\n          Value: true\n        - Alpha: 1\n          Autocompute Intensity Bounds: true\n          Autocompute Value Bounds:\n            Max Value: 10\n            Min Value: -10\n            Value: true\n          Axis: Z\n          Channel Name: intensity\n          Class: rviz/PointCloud2\n          Color: 255; 255; 255\n          Color Transformer: Intensity\n          Decay Time: 0\n          Enabled: false\n          Invert Rainbow: false\n          Max Color: 255; 255; 255\n          Max Intensity: 255\n          Min Color: 0; 0; 0\n          Min Intensity: 17\n          Name: current\n          Position Transformer: XYZ\n          Queue Size: 10\n          Selectable: true\n          Size (Pixels): 3\n          Size (m): 0.00999999978\n          Style: Flat Squares\n          Topic: /surfel_fusion/raw_pointcloud\n          Unreliable: false\n          Use Fixed Frame: true\n          Use rainbow: true\n          Value: false\n        - Alpha: 1\n          Autocompute Intensity Bounds: true\n          Autocompute Value Bounds:\n            Max Value: 10\n            Min Value: -10\n            Value: true\n          Axis: Z\n          Channel Name: z\n          Class: rviz/PointCloud2\n          Color: 255; 255; 255\n          Color Transformer: Intensity\n          Decay Time: 0\n          Enabled: false\n          Invert Rainbow: true\n          Max Color: 255; 255; 255\n          Max Intensity: 1.4563067\n          Min Color: 0; 0; 0\n          Min Intensity: -1.5742451\n          Name: filtered\n          Position Transformer: XYZ\n          Queue Size: 10\n          Selectable: true\n          Size (Pixels): 3\n          Size (m): 0.00999999978\n          Style: Points\n          Topic: /map_server/filtered_map\n          Unreliable: false\n          Use Fixed Frame: true\n          Use rainbow: true\n          Value: false\n      Enabled: true\n      Name: Surfel Mapping\n  Enabled: true\n  Global Options:\n    Background Color: 48; 48; 48\n    Fixed Frame: world\n    Frame Rate: 30\n  Name: root\n  Tools:\n    - Class: rviz/Interact\n      Hide Inactive Objects: true\n    - Class: rviz/MoveCamera\n    - Class: rviz/Select\n    - Class: rviz/FocusCamera\n    - Class: rviz/Measure\n    - Class: rviz/SetInitialPose\n      Topic: /initialpose\n    - Class: rviz/SetGoal\n      Topic: /move_base_simple/goal\n    - Class: rviz/PublishPoint\n      Single click: true\n      Topic: /clicked_point\n  Value: true\n  Views:\n    Current:\n      Class: rviz/Orbit\n      Distance: 88.2317963\n      Enable Stereo Rendering:\n        Stereo Eye Separation: 0.0599999987\n        Stereo Focal Distance: 1\n        Swap Stereo Eyes: false\n        Value: false\n      Focal Point:\n        X: 14.8281975\n        Y: -0.740946651\n        Z: 0.155234724\n      Focal Shape Fixed Size: false\n      Focal Shape Size: 0.0500000007\n      Name: Current View\n      Near Clip Distance: 0.00999999978\n      Pitch: 1.29479516\n      Target Frame: <Fixed Frame>\n      Value: Orbit (rviz)\n      Yaw: 4.72152758\n    Saved:\n      - Class: rviz/Orbit\n        Distance: 49.6446877\n        Enable Stereo Rendering:\n          Stereo Eye Separation: 0.0599999987\n          Stereo Focal Distance: 1\n          Swap Stereo Eyes: false\n          Value: false\n        Focal Point:\n          X: 22.8474731\n          Y: 20.4583664\n          Z: -6.84013748\n        Focal Shape Fixed Size: true\n        Focal Shape Size: 0.0500000007\n        Name: Orbit\n        Near Clip Distance: 0.00999999978\n        Pitch: 1.16479671\n        Target Frame: <Fixed Frame>\n        Value: Orbit (rviz)\n        Yaw: 5.02046442\nWindow Geometry:\n  AR_image_corrected:\n    collapsed: false\n  AR_image_odm:\n    collapsed: false\n  DBOW Match Image:\n    collapsed: false\n  Displays:\n    collapsed: false\n  Height: 1056\n  Hide Left Dock: false\n  Hide Right Dock: false\n  Input Image:\n    collapsed: false\n  QMainWindow State: 000000ff00000000fd00000004000000000000016a00000402fc020000001afb0000001200530065006c0065006300740069006f006e0000000019000000840000006400fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001b500000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afc000000000000013c0000001600fffffffa000000000100000001fb0000001e007200610077005f0069006e007000750074005f0069006d0061006700650100000000ffffffff000000a800fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d0061006700650100000051000000270000000000000000fb0000000a0049006d006100670065010000007e000000510000000000000000fb0000001a0043006c0075007300740065007200200041007300730067006e0100000357000000160000000000000000fb0000002c0050006f007300650020004700720061007000680020004d006100740063006800200049006d0061006700650100000373000000160000000000000000fb0000001e0043006f006c006f0063006100740069006f006e002000500061006900720000000000ffffffff0000000000000000fb0000000c004600720061006d006500730100000041000000420000000000000000fb000000160049006e00700075007400200049006d0061006700650000000041000001670000001600fffffffb000000200054007200610063006b006500640020004600650061007400750072006500730000000019000001340000001600fffffffb0000000a0049006d00610067006501000002c3000000c60000000000000000fb0000001a004c006f006f007000430061006e0064006900640061007400650100000373000000160000000000000000fb00000028006300650072006500620072006f0020002d00200069006d006100670065007000610069007200650100000142000000ce0000001600fffffffb00000032006400690073006a006f0069006e0074005f007300650074005f007300740061007400750073005f0069006d0061006700650100000216000000e20000001600fffffffb0000001800410052005f0069006d006100670065005f006f0064006d000000025b000000d60000001600fffffffb0000002400410052005f0069006d006100670065005f0063006f007200720065006300740065006401000002fe000001040000001600fffffffb0000002a00760069006e0073002d006d006f006e006f006d0061007400630068002d0069006d006100670065007300000002c4000000c50000000000000000fb00000020006c006f006f0070005f006d0061007400630068005f0049006d00610067006501000003b7000000160000000000000000fb0000000a0049006d0061006700650100000301000000cc0000000000000000fb0000002000440042004f00570020004d006100740063006800200049006d0061006700650000000334000000ce0000001600ffffff000000010000016a00000402fc0200000004fc0000000000000402000000fb0100001dfa000000010100000002fb0000000a005600690065007700730100000000ffffffff0000010f00fffffffb000000100044006900730070006c00610079007301000006140000016a0000016a00fffffffb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb000000100044006900730070006c0061007900730000000212000001bb000000dd00fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e100000197000000030000077e0000003bfc0100000002fb0000000800540069006d006500000000000000077e0000030000fffffffb0000000800540069006d006501000000000000045000000000000000000000045f0000040200000004000000040000000800000008fc000000030000000000000000000000020000000000000003000000010000000a0054006f006f006c00730000000000ffffffff0000000000000000\n  Selection:\n    collapsed: false\n  Time:\n    collapsed: false\n  Tool Properties:\n    collapsed: false\n  Tracked Features:\n    collapsed: false\n  Views:\n    collapsed: false\n  Width: 1855\n  X: 65\n  Y: 24\n  cerebro - imagepaire:\n    collapsed: false\n  disjoint_set_status_image:\n    collapsed: false\n  raw_input_image:\n    collapsed: false\n"
  },
  {
    "path": "scripts/TerminalColors.py",
    "content": "class bcolors:\n    HEADER = '\\033[95m'\n    OKBLUE = '\\033[94m'\n    OKGREEN = '\\033[92m'\n    WARNING = '\\033[93m'\n    FAIL = '\\033[91m'\n    ENDC = '\\033[0m'\n    BOLD = '\\033[1m'\n    UNDERLINE = '\\033[4m'\n\n\n# USage\n#```\n#    from TerminalColors import bcolors\n#    tcol = bcolors()\n#    print tcol.OKGREEN, \"hello in green color\", tcol.ENDC\n#```\n"
  },
  {
    "path": "scripts/keras.models/.gitignore",
    "content": ""
  },
  {
    "path": "scripts/keras.models/Apr2019/gray_conv6_K16__centeredinput/model.json",
    "content": "\"{\\\"class_name\\\": \\\"Model\\\", \\\"keras_version\\\": \\\"2.2.4\\\", \\\"config\\\": {\\\"layers\\\": [{\\\"class_name\\\": \\\"InputLayer\\\", \\\"config\\\": {\\\"dtype\\\": \\\"float32\\\", \\\"batch_input_shape\\\": [null, 240, 320, 1], \\\"name\\\": \\\"input_1\\\", \\\"sparse\\\": false}, \\\"inbound_nodes\\\": [], \\\"name\\\": \\\"input_1\\\"}, {\\\"class_name\\\": \\\"ZeroPadding2D\\\", \\\"config\\\": {\\\"padding\\\": [[0, 1], [0, 1]], \\\"trainable\\\": true, \\\"name\\\": \\\"conv1_pad\\\", \\\"data_format\\\": \\\"channels_last\\\"}, \\\"inbound_nodes\\\": [[[\\\"input_1\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv1_pad\\\"}, {\\\"class_name\\\": \\\"Conv2D\\\", \\\"config\\\": {\\\"kernel_constraint\\\": null, \\\"kernel_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"name\\\": \\\"conv1\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"trainable\\\": true, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"valid\\\", \\\"strides\\\": [2, 2], \\\"dilation_rate\\\": [1, 1], \\\"kernel_regularizer\\\": {\\\"class_name\\\": \\\"L1L2\\\", \\\"config\\\": {\\\"l2\\\": 9.999999747378752e-05, \\\"l1\\\": 0.0}}, \\\"filters\\\": 32, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"use_bias\\\": false, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv1_pad\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv1\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv1_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv1\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv1_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv1_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv1_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv1_relu\\\"}, {\\\"class_name\\\": \\\"DepthwiseConv2D\\\", \\\"config\\\": {\\\"use_bias\\\": false, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_1\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"depth_multiplier\\\": 1, \\\"depthwise_constraint\\\": null, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"depthwise_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"depthwise_regularizer\\\": null, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv1_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_1\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_dw_1_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_1\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_1_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_1_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_1_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_1_relu\\\"}, {\\\"class_name\\\": \\\"Conv2D\\\", \\\"config\\\": {\\\"kernel_constraint\\\": null, \\\"kernel_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"name\\\": \\\"conv_pw_1\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"trainable\\\": true, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"kernel_regularizer\\\": {\\\"class_name\\\": \\\"L1L2\\\", \\\"config\\\": {\\\"l2\\\": 9.999999747378752e-05, \\\"l1\\\": 0.0}}, \\\"filters\\\": 64, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"use_bias\\\": false, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [1, 1]}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_1_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_1\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_pw_1_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_1\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_1_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pw_1_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_1_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_1_relu\\\"}, {\\\"class_name\\\": \\\"ZeroPadding2D\\\", \\\"config\\\": {\\\"padding\\\": [[0, 1], [0, 1]], \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pad_2\\\", \\\"data_format\\\": \\\"channels_last\\\"}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_1_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pad_2\\\"}, {\\\"class_name\\\": \\\"DepthwiseConv2D\\\", \\\"config\\\": {\\\"use_bias\\\": false, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_2\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"depth_multiplier\\\": 1, \\\"depthwise_constraint\\\": null, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"valid\\\", \\\"strides\\\": [2, 2], \\\"dilation_rate\\\": [1, 1], \\\"depthwise_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"depthwise_regularizer\\\": null, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv_pad_2\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_2\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_dw_2_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_2\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_2_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_2_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_2_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_2_relu\\\"}, {\\\"class_name\\\": \\\"Conv2D\\\", \\\"config\\\": {\\\"kernel_constraint\\\": null, \\\"kernel_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"name\\\": \\\"conv_pw_2\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"trainable\\\": true, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"kernel_regularizer\\\": {\\\"class_name\\\": \\\"L1L2\\\", \\\"config\\\": {\\\"l2\\\": 9.999999747378752e-05, \\\"l1\\\": 0.0}}, \\\"filters\\\": 128, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"use_bias\\\": false, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [1, 1]}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_2_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_2\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_pw_2_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_2\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_2_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pw_2_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_2_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_2_relu\\\"}, {\\\"class_name\\\": \\\"DepthwiseConv2D\\\", \\\"config\\\": {\\\"use_bias\\\": false, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_3\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"depth_multiplier\\\": 1, \\\"depthwise_constraint\\\": null, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"depthwise_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"depthwise_regularizer\\\": null, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_2_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_3\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_dw_3_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_3\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_3_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_3_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_3_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_3_relu\\\"}, {\\\"class_name\\\": \\\"Conv2D\\\", \\\"config\\\": {\\\"kernel_constraint\\\": null, \\\"kernel_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"name\\\": \\\"conv_pw_3\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"trainable\\\": true, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"kernel_regularizer\\\": {\\\"class_name\\\": \\\"L1L2\\\", \\\"config\\\": {\\\"l2\\\": 9.999999747378752e-05, \\\"l1\\\": 0.0}}, \\\"filters\\\": 128, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"use_bias\\\": false, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [1, 1]}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_3_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_3\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_pw_3_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_3\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_3_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pw_3_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_3_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_3_relu\\\"}, {\\\"class_name\\\": \\\"ZeroPadding2D\\\", \\\"config\\\": {\\\"padding\\\": [[0, 1], [0, 1]], \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pad_4\\\", \\\"data_format\\\": \\\"channels_last\\\"}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_3_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pad_4\\\"}, {\\\"class_name\\\": \\\"DepthwiseConv2D\\\", \\\"config\\\": {\\\"use_bias\\\": false, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_4\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"depth_multiplier\\\": 1, \\\"depthwise_constraint\\\": null, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"valid\\\", \\\"strides\\\": [2, 2], \\\"dilation_rate\\\": [1, 1], \\\"depthwise_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"depthwise_regularizer\\\": null, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv_pad_4\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_4\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_dw_4_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_4\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_4_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_4_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_4_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_4_relu\\\"}, {\\\"class_name\\\": \\\"Conv2D\\\", \\\"config\\\": {\\\"kernel_constraint\\\": null, \\\"kernel_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"name\\\": \\\"conv_pw_4\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"trainable\\\": true, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"kernel_regularizer\\\": {\\\"class_name\\\": \\\"L1L2\\\", \\\"config\\\": {\\\"l2\\\": 9.999999747378752e-05, \\\"l1\\\": 0.0}}, \\\"filters\\\": 256, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"use_bias\\\": false, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [1, 1]}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_4_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_4\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_pw_4_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_4\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_4_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pw_4_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_4_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_4_relu\\\"}, {\\\"class_name\\\": \\\"DepthwiseConv2D\\\", \\\"config\\\": {\\\"use_bias\\\": false, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_5\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"depth_multiplier\\\": 1, \\\"depthwise_constraint\\\": null, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"depthwise_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"depthwise_regularizer\\\": null, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_4_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_5\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_dw_5_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_5\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_5_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_5_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_5_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_5_relu\\\"}, {\\\"class_name\\\": \\\"Conv2D\\\", \\\"config\\\": {\\\"kernel_constraint\\\": null, \\\"kernel_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"name\\\": \\\"conv_pw_5\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"trainable\\\": true, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"kernel_regularizer\\\": {\\\"class_name\\\": \\\"L1L2\\\", \\\"config\\\": {\\\"l2\\\": 9.999999747378752e-05, \\\"l1\\\": 0.0}}, \\\"filters\\\": 256, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"use_bias\\\": false, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [1, 1]}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_5_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_5\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_pw_5_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_5\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_5_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pw_5_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_5_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_5_relu\\\"}, {\\\"class_name\\\": \\\"ZeroPadding2D\\\", \\\"config\\\": {\\\"padding\\\": [[0, 1], [0, 1]], \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pad_6\\\", \\\"data_format\\\": \\\"channels_last\\\"}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_5_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pad_6\\\"}, {\\\"class_name\\\": \\\"DepthwiseConv2D\\\", \\\"config\\\": {\\\"use_bias\\\": false, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_6\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"depth_multiplier\\\": 1, \\\"depthwise_constraint\\\": null, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"valid\\\", \\\"strides\\\": [2, 2], \\\"dilation_rate\\\": [1, 1], \\\"depthwise_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"depthwise_regularizer\\\": null, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv_pad_6\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_6\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_dw_6_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_6\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_6_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_6_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_6_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_6_relu\\\"}, {\\\"class_name\\\": \\\"NetVLADLayer\\\", \\\"config\\\": {\\\"num_clusters\\\": 16, \\\"trainable\\\": true, \\\"name\\\": \\\"net_vlad_layer_1\\\"}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_6_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"net_vlad_layer_1\\\"}], \\\"input_layers\\\": [[\\\"input_1\\\", 0, 0]], \\\"output_layers\\\": [[\\\"net_vlad_layer_1\\\", 0, 0]], \\\"name\\\": \\\"model_1\\\"}, \\\"backend\\\": \\\"tensorflow\\\"}\""
  },
  {
    "path": "scripts/keras.models/README.md",
    "content": "# Pretrained Models for Place Recognition.\n\nThis folder contains tested models for place recognition. The models have been tested for real-time\nperformance.\n\nthe files a) mobilenet_conv7_allpairloss.keras b) model.json c) modelarch_and_weights.h5 d) core_model.keras e) xcore.png are associated with the default model.\n"
  },
  {
    "path": "scripts/keras.models/model.json",
    "content": "\"{\\\"class_name\\\": \\\"Model\\\", \\\"keras_version\\\": \\\"2.2.4\\\", \\\"config\\\": {\\\"layers\\\": [{\\\"class_name\\\": \\\"InputLayer\\\", \\\"config\\\": {\\\"dtype\\\": \\\"float32\\\", \\\"batch_input_shape\\\": [null, 480, 752, 3], \\\"name\\\": \\\"input_1\\\", \\\"sparse\\\": false}, \\\"inbound_nodes\\\": [], \\\"name\\\": \\\"input_1\\\"}, {\\\"class_name\\\": \\\"ZeroPadding2D\\\", \\\"config\\\": {\\\"padding\\\": [[0, 1], [0, 1]], \\\"trainable\\\": true, \\\"name\\\": \\\"conv1_pad\\\", \\\"data_format\\\": \\\"channels_last\\\"}, \\\"inbound_nodes\\\": [[[\\\"input_1\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv1_pad\\\"}, {\\\"class_name\\\": \\\"Conv2D\\\", \\\"config\\\": {\\\"kernel_constraint\\\": null, \\\"kernel_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"name\\\": \\\"conv1\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"trainable\\\": true, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"valid\\\", \\\"strides\\\": [2, 2], \\\"dilation_rate\\\": [1, 1], \\\"kernel_regularizer\\\": null, \\\"filters\\\": 32, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"use_bias\\\": false, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv1_pad\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv1\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv1_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv1\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv1_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv1_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv1_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv1_relu\\\"}, {\\\"class_name\\\": \\\"DepthwiseConv2D\\\", \\\"config\\\": {\\\"use_bias\\\": false, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_1\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"depth_multiplier\\\": 1, \\\"depthwise_constraint\\\": null, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"depthwise_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"depthwise_regularizer\\\": null, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv1_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_1\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_dw_1_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_1\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_1_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_1_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_1_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_1_relu\\\"}, {\\\"class_name\\\": \\\"Conv2D\\\", \\\"config\\\": {\\\"kernel_constraint\\\": null, \\\"kernel_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"name\\\": \\\"conv_pw_1\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"trainable\\\": true, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"kernel_regularizer\\\": null, \\\"filters\\\": 64, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"use_bias\\\": false, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [1, 1]}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_1_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_1\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_pw_1_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_1\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_1_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pw_1_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_1_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_1_relu\\\"}, {\\\"class_name\\\": \\\"ZeroPadding2D\\\", \\\"config\\\": {\\\"padding\\\": [[0, 1], [0, 1]], \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pad_2\\\", \\\"data_format\\\": \\\"channels_last\\\"}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_1_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pad_2\\\"}, {\\\"class_name\\\": \\\"DepthwiseConv2D\\\", \\\"config\\\": {\\\"use_bias\\\": false, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_2\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"depth_multiplier\\\": 1, \\\"depthwise_constraint\\\": null, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"valid\\\", \\\"strides\\\": [2, 2], \\\"dilation_rate\\\": [1, 1], \\\"depthwise_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"depthwise_regularizer\\\": null, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv_pad_2\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_2\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_dw_2_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_2\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_2_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_2_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_2_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_2_relu\\\"}, {\\\"class_name\\\": \\\"Conv2D\\\", \\\"config\\\": {\\\"kernel_constraint\\\": null, \\\"kernel_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"name\\\": \\\"conv_pw_2\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"trainable\\\": true, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"kernel_regularizer\\\": null, \\\"filters\\\": 128, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"use_bias\\\": false, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [1, 1]}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_2_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_2\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_pw_2_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_2\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_2_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pw_2_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_2_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_2_relu\\\"}, {\\\"class_name\\\": \\\"DepthwiseConv2D\\\", \\\"config\\\": {\\\"use_bias\\\": false, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_3\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"depth_multiplier\\\": 1, \\\"depthwise_constraint\\\": null, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"depthwise_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"depthwise_regularizer\\\": null, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_2_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_3\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_dw_3_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_3\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_3_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_3_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_3_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_3_relu\\\"}, {\\\"class_name\\\": \\\"Conv2D\\\", \\\"config\\\": {\\\"kernel_constraint\\\": null, \\\"kernel_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"name\\\": \\\"conv_pw_3\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"trainable\\\": true, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"kernel_regularizer\\\": null, \\\"filters\\\": 128, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"use_bias\\\": false, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [1, 1]}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_3_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_3\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_pw_3_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_3\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_3_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pw_3_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_3_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_3_relu\\\"}, {\\\"class_name\\\": \\\"ZeroPadding2D\\\", \\\"config\\\": {\\\"padding\\\": [[0, 1], [0, 1]], \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pad_4\\\", \\\"data_format\\\": \\\"channels_last\\\"}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_3_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pad_4\\\"}, {\\\"class_name\\\": \\\"DepthwiseConv2D\\\", \\\"config\\\": {\\\"use_bias\\\": false, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_4\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"depth_multiplier\\\": 1, \\\"depthwise_constraint\\\": null, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"valid\\\", \\\"strides\\\": [2, 2], \\\"dilation_rate\\\": [1, 1], \\\"depthwise_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"depthwise_regularizer\\\": null, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv_pad_4\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_4\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_dw_4_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_4\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_4_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_4_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_4_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_4_relu\\\"}, {\\\"class_name\\\": \\\"Conv2D\\\", \\\"config\\\": {\\\"kernel_constraint\\\": null, \\\"kernel_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"name\\\": \\\"conv_pw_4\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"trainable\\\": true, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"kernel_regularizer\\\": null, \\\"filters\\\": 256, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"use_bias\\\": false, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [1, 1]}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_4_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_4\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_pw_4_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_4\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_4_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pw_4_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_4_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_4_relu\\\"}, {\\\"class_name\\\": \\\"DepthwiseConv2D\\\", \\\"config\\\": {\\\"use_bias\\\": false, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_5\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"depth_multiplier\\\": 1, \\\"depthwise_constraint\\\": null, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"depthwise_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"depthwise_regularizer\\\": null, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_4_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_5\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_dw_5_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_5\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_5_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_5_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_5_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_5_relu\\\"}, {\\\"class_name\\\": \\\"Conv2D\\\", \\\"config\\\": {\\\"kernel_constraint\\\": null, \\\"kernel_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"name\\\": \\\"conv_pw_5\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"trainable\\\": true, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"kernel_regularizer\\\": null, \\\"filters\\\": 256, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"use_bias\\\": false, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [1, 1]}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_5_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_5\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_pw_5_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_5\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_5_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pw_5_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_5_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_5_relu\\\"}, {\\\"class_name\\\": \\\"ZeroPadding2D\\\", \\\"config\\\": {\\\"padding\\\": [[0, 1], [0, 1]], \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pad_6\\\", \\\"data_format\\\": \\\"channels_last\\\"}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_5_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pad_6\\\"}, {\\\"class_name\\\": \\\"DepthwiseConv2D\\\", \\\"config\\\": {\\\"use_bias\\\": false, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_6\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"depth_multiplier\\\": 1, \\\"depthwise_constraint\\\": null, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"valid\\\", \\\"strides\\\": [2, 2], \\\"dilation_rate\\\": [1, 1], \\\"depthwise_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"depthwise_regularizer\\\": null, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv_pad_6\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_6\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_dw_6_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_6\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_6_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_6_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_6_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_6_relu\\\"}, {\\\"class_name\\\": \\\"Conv2D\\\", \\\"config\\\": {\\\"kernel_constraint\\\": null, \\\"kernel_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"name\\\": \\\"conv_pw_6\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"trainable\\\": true, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"kernel_regularizer\\\": null, \\\"filters\\\": 512, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"use_bias\\\": false, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [1, 1]}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_6_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_6\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_pw_6_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_6\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_6_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pw_6_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_6_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_6_relu\\\"}, {\\\"class_name\\\": \\\"DepthwiseConv2D\\\", \\\"config\\\": {\\\"use_bias\\\": false, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_7\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"depth_multiplier\\\": 1, \\\"depthwise_constraint\\\": null, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"depthwise_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"depthwise_regularizer\\\": null, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [3, 3]}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_6_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_7\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_dw_7_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_7\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_7_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_dw_7_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_7_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_dw_7_relu\\\"}, {\\\"class_name\\\": \\\"Conv2D\\\", \\\"config\\\": {\\\"kernel_constraint\\\": null, \\\"kernel_initializer\\\": {\\\"class_name\\\": \\\"VarianceScaling\\\", \\\"config\\\": {\\\"distribution\\\": \\\"uniform\\\", \\\"scale\\\": 1.0, \\\"seed\\\": null, \\\"mode\\\": \\\"fan_avg\\\"}}, \\\"name\\\": \\\"conv_pw_7\\\", \\\"bias_regularizer\\\": null, \\\"bias_constraint\\\": null, \\\"activation\\\": \\\"linear\\\", \\\"trainable\\\": true, \\\"data_format\\\": \\\"channels_last\\\", \\\"padding\\\": \\\"same\\\", \\\"strides\\\": [1, 1], \\\"dilation_rate\\\": [1, 1], \\\"kernel_regularizer\\\": null, \\\"filters\\\": 512, \\\"bias_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"use_bias\\\": false, \\\"activity_regularizer\\\": null, \\\"kernel_size\\\": [1, 1]}, \\\"inbound_nodes\\\": [[[\\\"conv_dw_7_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_7\\\"}, {\\\"class_name\\\": \\\"BatchNormalization\\\", \\\"config\\\": {\\\"beta_constraint\\\": null, \\\"gamma_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"moving_mean_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"name\\\": \\\"conv_pw_7_bn\\\", \\\"epsilon\\\": 0.001, \\\"trainable\\\": true, \\\"moving_variance_initializer\\\": {\\\"class_name\\\": \\\"Ones\\\", \\\"config\\\": {}}, \\\"beta_initializer\\\": {\\\"class_name\\\": \\\"Zeros\\\", \\\"config\\\": {}}, \\\"scale\\\": true, \\\"axis\\\": -1, \\\"gamma_constraint\\\": null, \\\"gamma_regularizer\\\": null, \\\"beta_regularizer\\\": null, \\\"momentum\\\": 0.99, \\\"center\\\": true}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_7\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_7_bn\\\"}, {\\\"class_name\\\": \\\"ReLU\\\", \\\"config\\\": {\\\"threshold\\\": 0.0, \\\"max_value\\\": 6.0, \\\"trainable\\\": true, \\\"name\\\": \\\"conv_pw_7_relu\\\", \\\"negative_slope\\\": 0.0}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_7_bn\\\", 0, 0, {}]]], \\\"name\\\": \\\"conv_pw_7_relu\\\"}, {\\\"class_name\\\": \\\"NetVLADLayer\\\", \\\"config\\\": {\\\"num_clusters\\\": 16, \\\"trainable\\\": true, \\\"name\\\": \\\"net_vlad_layer_1\\\"}, \\\"inbound_nodes\\\": [[[\\\"conv_pw_7_relu\\\", 0, 0, {}]]], \\\"name\\\": \\\"net_vlad_layer_1\\\"}], \\\"input_layers\\\": [[\\\"input_1\\\", 0, 0]], \\\"output_layers\\\": [[\\\"net_vlad_layer_1\\\", 0, 0]], \\\"name\\\": \\\"model_1\\\"}, \\\"backend\\\": \\\"tensorflow\\\"}\""
  },
  {
    "path": "scripts/keras_helpers.py",
    "content": "from keras import backend as K\nfrom keras.engine.topology import Layer\nimport keras\nimport code\nimport numpy as np\n\nimport cv2\nimport code\n\n\n#---------------------------------------------------------------------------------\n# My Layers\n#   NetVLADLayer\n#---------------------------------------------------------------------------------\n\n# # Writing your own custom layers\n# class MyLayer(Layer):\n#\n#     def __init__(self, output_dim, **kwargs):\n#         self.output_dim = output_dim\n#         super(MyLayer, self).__init__(**kwargs)\n#\n#     def build(self, input_shape):\n#         # Create a trainable weight variable for this layer.\n#         self.kernel = self.add_weight(name='kernel',\n#                                       shape=(input_shape[1], self.output_dim),\n#                                       initializer='uniform',\n#                                       trainable=True)\n#         super(MyLayer, self).build(input_shape)  # Be sure to call this at the end\n#\n#     def call(self, x):\n#         return [K.dot(x, self.kernel), K.dot(x, self.kernel)]\n#\n#     def compute_output_shape(self, input_shape):\n#         return [(input_shape[0], self.output_dim), (input_shape[0], self.output_dim)]\n#\n#\n# class NetVLADLayer( Layer ):\n#\n#     def __init__( self, num_clusters, **kwargs ):\n#         self.num_clusters = num_clusters\n#         super(NetVLADLayer, self).__init__(**kwargs)\n#\n#     def build( self, input_shape ):\n#         self.K = self.num_clusters\n#         self.D = input_shape[-1]\n#\n#         self.kernel = self.add_weight( name='kernel',\n#                                     shape=(1,1,self.D,self.K),\n#                                     initializer='uniform',\n#                                     trainable=True )\n#\n#         self.bias = self.add_weight( name='bias',\n#                                     shape=(1,1,self.K),\n#                                     initializer='uniform',\n#                                     trainable=True )\n#\n#         self.C = self.add_weight( name='cluster_centers',\n#                                 shape=[1,1,1,self.D,self.K],\n#                                 initializer='uniform',\n#                                 trainable=True)\n#\n#     def call( self, x ):\n#         # soft-assignment.\n#         s = K.conv2d( x, self.kernel, padding='same' ) + self.bias\n#         a = K.softmax( s )\n#         self.amap = K.argmax( a, -1 )\n#         print 'amap.shape', self.amap.shape\n#\n#         # Dims used hereafter: batch, H, W, desc_coeff, cluster\n#         a = K.expand_dims( a, -2 )\n#         # print 'a.shape=',a.shape\n#\n#         # Core\n#         v = K.expand_dims(x, -1) + self.C\n#         # print 'v.shape', v.shape\n#         v = a * v\n#         # print 'v.shape', v.shape\n#         v = K.sum(v, axis=[1, 2])\n#         # print 'v.shape', v.shape\n#         v = K.permute_dimensions(v, pattern=[0, 2, 1])\n#         # print 'v.shape', v.shape\n#         #v.shape = None x K x D\n#\n#         # Normalize v (Intra Normalization)\n#         v = K.l2_normalize( v, axis=-1 )\n#         v = K.batch_flatten( v )\n#         v = K.l2_normalize( v, axis=-1 )\n#\n#         return [v, self.amap]\n#\n#     def compute_output_shape( self, input_shape ):\n#         # return (input_shape[0], self.v.shape[-1].value )\n#         # return [(input_shape[0], self.K*self.D ), (input_shape[0], self.amap.shape[1].value, self.amap.shape[2].value) ]\n#         return [(input_shape[0], self.K*self.D ), (input_shape[0], input_shape[1], input_shape[2]) ]\n\n\n#--------------------------------------------------------------------------------\n# Base CNNs\n#--------------------------------------------------------------------------------\n\n# def make_vgg( input_img ):\n#     r_l2=keras.regularizers.l2(0.01)\n#     r_l1=keras.regularizers.l1(0.01)\n#\n#     x_64 = keras.layers.Conv2D( 64, (3,3), padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( input_img )\n#     x_64 = keras.layers.normalization.BatchNormalization()( x_64 )\n#     x_64 = keras.layers.Conv2D( 64, (3,3), padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_64 )\n#     x_64 = keras.layers.normalization.BatchNormalization()( x_64 )\n#     x_64 = keras.layers.MaxPooling2D( pool_size=(2,2), padding='same' )( x_64 )\n#\n#\n#     x_128 = keras.layers.Conv2D( 128, (3,3), padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_64 )\n#     x_128 = keras.layers.normalization.BatchNormalization()( x_128 )\n#     x_128 = keras.layers.Conv2D( 128, (3,3), padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_128 )\n#     x_128 = keras.layers.normalization.BatchNormalization()( x_128 )\n#     x_128 = keras.layers.MaxPooling2D( pool_size=(2,2), padding='same' )( x_128 )\n#\n#\n#     # x_256 = keras.layers.Conv2D( 256, (3,3), padding='same', activation='relu' )( x_128 )\n#     # x_256 = keras.layers.normalization.BatchNormalization()( x_256 )\n#     # x_256 = keras.layers.Conv2D( 256, (3,3), padding='same', activation='relu' )( x_256 )\n#     # x_256 = keras.layers.normalization.BatchNormalization()( x_256 )\n#     # x_256 = keras.layers.MaxPooling2D( pool_size=(2,2), padding='same' )( x_256 )\n#\n#     #\n#     # x_512 = keras.layers.Conv2D( 512, (3,3), padding='same', activation='relu' )( x_256 )\n#     # # BN\n#     # x_512 = keras.layers.Conv2D( 512, (3,3), padding='same', activation='relu' )( x_512 )\n#     # # BN\n#     # x_512 = keras.layers.MaxPooling2D( pool_size=(2,2), padding='same' )( x_512 )\n#\n#\n#     x = keras.layers.Conv2DTranspose( 32, (5,5), strides=4, padding='same' )( x_128 )\n#     # x = x_128\n#\n#     return x\n#\n#\n# def make_upsampling_vgg( input_img  ):\n#     r_l2=keras.regularizers.l2(0.01)\n#     r_l1=keras.regularizers.l1(0.01)\n#\n#     x_64 = keras.layers.Conv2D( 64, (3,3), padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( input_img )\n#     x_64 = keras.layers.normalization.BatchNormalization()( x_64 )\n#     x_64 = keras.layers.Conv2D( 64, (3,3), strides=2, padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_64 )\n#     x_64 = keras.layers.normalization.BatchNormalization()( x_64 )\n#\n#     x_128 = keras.layers.Conv2D( 128, (3,3), padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_64 )\n#     x_128 = keras.layers.normalization.BatchNormalization()( x_128 )\n#     x_128 = keras.layers.Conv2D( 128, (3,3), strides=2, padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_128 )\n#     x_128 = keras.layers.normalization.BatchNormalization()( x_128 )\n#\n#     x_256 = keras.layers.Conv2D( 128, (3,3), padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_128 )\n#     x_256 = keras.layers.normalization.BatchNormalization()( x_256 )\n#     x_256 = keras.layers.Conv2D( 128, (3,3), strides=2, padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_256 )\n#     x_256 = keras.layers.normalization.BatchNormalization()( x_256 )\n#\n#     z = keras.layers.Conv2DTranspose( 32, (11,11), strides=8, padding='same' )( x_256 )\n#     x = keras.layers.Conv2DTranspose( 32, (9,9), strides=4, padding='same' )( x_128 )\n#     y = keras.layers.Conv2DTranspose( 32, (7,7), strides=2, padding='same' )( x_64 )\n#\n#     out = keras.layers.Add()( [x,y,z] )\n#     return out\n#\n# def make_from_vgg19( input_img, trainable=True, weights=None ):\n#     base_model = keras.applications.vgg19.VGG19(weights=weights, include_top=False, input_tensor=input_img)\n#\n#     for l in base_model.layers:\n#         l.trainable = trainable\n#\n#     base_model_out = base_model.get_layer('block2_pool').output\n#\n#     z = keras.layers.Conv2DTranspose( 32, (9,9), strides=4, padding='same' )( base_model_out )\n#     return z\n#\n# def make_from_vgg19_multiconvup( input_img, trainable=True, weights=None ):\n#     base_model = keras.applications.vgg19.VGG19(weights=weights, include_top=False, input_tensor=input_img)\n#\n#     for l in base_model.layers:\n#         l.trainable = trainable\n#         #TODO : add kernel regularizers and activity_regularizer to conv layers\n#\n#     base_model_out = base_model.get_layer('block2_pool').output\n#\n#     up_conv_out = keras.layers.Conv2DTranspose( 32, (9,9), strides=2, padding='same', activation='relu' )( base_model_out )\n#     up_conv_out = keras.layers.normalization.BatchNormalization()( up_conv_out )\n#\n#     up_conv_out = keras.layers.Conv2DTranspose( 32, (9,9), strides=2, padding='same', activation='relu' )( up_conv_out )\n#     up_conv_out = keras.layers.normalization.BatchNormalization()( up_conv_out )\n#\n#\n#     return up_conv_out\n#\n#\n# def make_from_mobilenet( input_img=None, weights=None ):\n#     # input_img = keras.layers.BatchNormalization()(input_img)\n#\n#     base_model = keras.applications.mobilenet.MobileNet( weights=weights, include_top=False, input_tensor=input_img )\n#     # base_model = keras.applications.mobilenet.MobileNet( weights=None, include_top=False, input_tensor=input_img )\n#     # keras.utils.plot_model( base_model, to_file='base_model.png', show_shapes=True )\n#\n#     # Pull out a layer from original network\n#     base_model_out = base_model.get_layer( 'conv_pw_7_relu').output # can also try conv_pw_7_relu etc.\n#\n#     # Up-sample\n#     # Basic idea is to try upsampling without using the transposed-conv layers. Instead use either of\n#     #   a) upsampling2d\n#     #   b) depth to space\n#     # followed by CBR (conv-BN-relu)\n#     # TODO\n#     ups_out = base_model_out\n#     # ups_out = keras.layers.UpSampling2D( size=(4,4) )( base_model_out )\n#\n#     # model = keras.models.Model( inputs=input_img, outputs=ups_out )\n#     # model.summary()\n#     # keras.utils.plot_model( model, to_file='base_model.png', show_shapes=True )\n#     # code.interact( local=locals() )\n#\n#     return ups_out\n#\n#\n\n\n\n\n#--------------------------------------------------------------------------------\n# Base CNNs\n#--------------------------------------------------------------------------------\n\ndef make_vgg( input_img ):\n    r_l2=keras.regularizers.l2(0.01)\n    r_l1=keras.regularizers.l1(0.01)\n\n    x_64 = keras.layers.Conv2D( 64, (3,3), padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( input_img )\n    x_64 = keras.layers.normalization.BatchNormalization()( x_64 )\n    x_64 = keras.layers.Conv2D( 64, (3,3), padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_64 )\n    x_64 = keras.layers.normalization.BatchNormalization()( x_64 )\n    x_64 = keras.layers.MaxPooling2D( pool_size=(2,2), padding='same' )( x_64 )\n\n\n    x_128 = keras.layers.Conv2D( 128, (3,3), padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_64 )\n    x_128 = keras.layers.normalization.BatchNormalization()( x_128 )\n    x_128 = keras.layers.Conv2D( 128, (3,3), padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_128 )\n    x_128 = keras.layers.normalization.BatchNormalization()( x_128 )\n    x_128 = keras.layers.MaxPooling2D( pool_size=(2,2), padding='same' )( x_128 )\n\n\n    # x_256 = keras.layers.Conv2D( 256, (3,3), padding='same', activation='relu' )( x_128 )\n    # x_256 = keras.layers.normalization.BatchNormalization()( x_256 )\n    # x_256 = keras.layers.Conv2D( 256, (3,3), padding='same', activation='relu' )( x_256 )\n    # x_256 = keras.layers.normalization.BatchNormalization()( x_256 )\n    # x_256 = keras.layers.MaxPooling2D( pool_size=(2,2), padding='same' )( x_256 )\n\n    #\n    # x_512 = keras.layers.Conv2D( 512, (3,3), padding='same', activation='relu' )( x_256 )\n    # # BN\n    # x_512 = keras.layers.Conv2D( 512, (3,3), padding='same', activation='relu' )( x_512 )\n    # # BN\n    # x_512 = keras.layers.MaxPooling2D( pool_size=(2,2), padding='same' )( x_512 )\n\n\n    x = keras.layers.Conv2DTranspose( 32, (5,5), strides=4, padding='same' )( x_128 )\n    # x = x_128\n\n    return x\n\n\ndef make_upsampling_vgg( input_img  ):\n    r_l2=keras.regularizers.l2(0.01)\n    r_l1=keras.regularizers.l1(0.01)\n\n    x_64 = keras.layers.Conv2D( 64, (3,3), padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( input_img )\n    x_64 = keras.layers.normalization.BatchNormalization()( x_64 )\n    x_64 = keras.layers.Conv2D( 64, (3,3), strides=2, padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_64 )\n    x_64 = keras.layers.normalization.BatchNormalization()( x_64 )\n\n    x_128 = keras.layers.Conv2D( 128, (3,3), padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_64 )\n    x_128 = keras.layers.normalization.BatchNormalization()( x_128 )\n    x_128 = keras.layers.Conv2D( 128, (3,3), strides=2, padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_128 )\n    x_128 = keras.layers.normalization.BatchNormalization()( x_128 )\n\n    x_256 = keras.layers.Conv2D( 128, (3,3), padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_128 )\n    x_256 = keras.layers.normalization.BatchNormalization()( x_256 )\n    x_256 = keras.layers.Conv2D( 128, (3,3), strides=2, padding='same', activation='relu', kernel_regularizer=r_l2, activity_regularizer=r_l1 )( x_256 )\n    x_256 = keras.layers.normalization.BatchNormalization()( x_256 )\n\n    z = keras.layers.Conv2DTranspose( 32, (11,11), strides=8, padding='same' )( x_256 )\n    x = keras.layers.Conv2DTranspose( 32, (9,9), strides=4, padding='same' )( x_128 )\n    y = keras.layers.Conv2DTranspose( 32, (7,7), strides=2, padding='same' )( x_64 )\n\n    out = keras.layers.Add()( [x,y,z] )\n    return out\n\n\n\ndef make_from_vgg19_multiconvup( input_img, trainable=False ):\n    base_model = keras.applications.vgg19.VGG19(weights='imagenet', include_top=False, input_tensor=input_img)\n\n    for l in base_model.layers:\n        l.trainable = trainable\n        #TODO : add kernel regularizers and activity_regularizer to conv layers\n\n    base_model_out = base_model.get_layer('block2_pool').output\n\n    up_conv_out = keras.layers.Conv2DTranspose( 32, (9,9), strides=2, padding='same', activation='relu' )( base_model_out )\n    up_conv_out = keras.layers.normalization.BatchNormalization()( up_conv_out )\n\n    up_conv_out = keras.layers.Conv2DTranspose( 32, (9,9), strides=2, padding='same', activation='relu' )( up_conv_out )\n    up_conv_out = keras.layers.normalization.BatchNormalization()( up_conv_out )\n\n    return up_conv_out\n\n\ndef make_from_mobilenet( input_img, weights=None, layer_name='conv_pw_7_relu' ):\n    # input_img = keras.layers.BatchNormalization()(input_img)\n\n    base_model = keras.applications.mobilenet.MobileNet( weights=weights, include_top=False, input_tensor=input_img )\n\n    # Pull out a layer from original network\n    base_model_out = base_model.get_layer( layer_name ).output # can also try conv_pw_7_relu etc.\n\n    return base_model_out\n\n\n\n\n\ndef make_from_vgg16( input_img, weights='imagenet', trainable=False, layer_name='block2_pool' ):\n    base_model = keras.applications.vgg16.VGG16(weights=weights, include_top=False, input_tensor=input_img)\n\n    for l in base_model.layers:\n        l.trainable = trainable\n\n    base_model_out = base_model.get_layer(layer_name).output\n    return base_model_out\n"
  },
  {
    "path": "scripts/predict_utils.py",
    "content": "import keras\nimport json\nimport pprint\nimport numpy as np\nimport cv2\nimport code\nfrom keras import backend as K\nfrom keras.engine.topology import Layer\n\n# This was copied from the training code's CustomNets.py\nclass NetVLADLayer( Layer ):\n\n    def __init__( self, num_clusters, **kwargs ):\n        self.num_clusters = num_clusters\n        super(NetVLADLayer, self).__init__(**kwargs)\n\n    def build( self, input_shape ):\n        self.K = self.num_clusters\n        self.D = input_shape[-1]\n\n        self.kernel = self.add_weight( name='kernel',\n                                    shape=(1,1,self.D,self.K),\n                                    initializer='uniform',\n                                    trainable=True )\n\n        self.bias = self.add_weight( name='bias',\n                                    shape=(1,1,self.K),\n                                    initializer='uniform',\n                                    trainable=True )\n\n        self.C = self.add_weight( name='cluster_centers',\n                                shape=[1,1,1,self.D,self.K],\n                                initializer='uniform',\n                                trainable=True)\n\n    def call( self, x ):\n        # soft-assignment.\n        s = K.conv2d( x, self.kernel, padding='same' ) + self.bias\n        a = K.softmax( s )\n        self.amap = K.argmax( a, -1 )\n        # print 'amap.shape', self.amap.shape\n\n        # Dims used hereafter: batch, H, W, desc_coeff, cluster\n        a = K.expand_dims( a, -2 )\n        # print 'a.shape=',a.shape\n\n        # Core\n        v = K.expand_dims(x, -1) + self.C\n        # print 'v.shape', v.shape\n        v = a * v\n        # print 'v.shape', v.shape\n        v = K.sum(v, axis=[1, 2])\n        # print 'v.shape', v.shape\n        v = K.permute_dimensions(v, pattern=[0, 2, 1])\n        # print 'v.shape', v.shape\n        #v.shape = None x K x D\n\n        # Normalize v (Intra Normalization)\n        v = K.l2_normalize( v, axis=-1 )\n        v = K.batch_flatten( v )\n        v = K.l2_normalize( v, axis=-1 )\n\n        # return [v, self.amap]\n        return v\n\n    def compute_output_shape( self, input_shape ):\n\n        # return [(input_shape[0], self.K*self.D ), (input_shape[0], input_shape[1], input_shape[2]) ]\n        return (input_shape[0], self.K*self.D )\n\n    def get_config( self ):\n        pass\n        # base_config = super(NetVLADLayer, self).get_config()\n        # return dict(list(base_config.items()))\n\n        # As suggested by: https://github.com/keras-team/keras/issues/4871#issuecomment-269731817\n        config = {'num_clusters': self.num_clusters}\n        base_config = super(NetVLADLayer, self).get_config()\n        return dict(list(base_config.items()) + list(config.items()))\n\n\n\nclass GhostVLADLayer( Layer ):\n\n    def __init__( self, num_clusters, num_ghost_clusters, **kwargs ):\n        self.num_clusters = num_clusters\n        self.num_ghost_clusters = num_ghost_clusters\n        super(GhostVLADLayer, self).__init__(**kwargs)\n\n    def build( self, input_shape ):\n        # self.K = self.num_clusters\n        self.K = self.num_clusters + self.num_ghost_clusters\n        self.D = input_shape[-1]\n\n        self.kernel = self.add_weight( name='kernel',\n                                    shape=(1,1,self.D,self.K),\n                                    initializer='uniform',\n                                    trainable=True )\n\n        self.bias = self.add_weight( name='bias',\n                                    shape=(1,1,self.K),\n                                    initializer='uniform',\n                                    trainable=True )\n\n        self.C = self.add_weight( name='cluster_centers',\n                                shape=[1,1,1,self.D,self.K],\n                                initializer='uniform',\n                                trainable=True)\n\n    def call( self, x ):\n        # soft-assignment.\n        s = K.conv2d( x, self.kernel, padding='same' ) + self.bias\n        a = K.softmax( s )\n        self.amap = K.argmax( a, -1 )\n        # print 'amap.shape', self.amap.shape\n\n        # Dims used hereafter: batch, H, W, desc_coeff, cluster\n        a = K.expand_dims( a, -2 )\n        # print 'a.shape=',a.shape\n\n        # Core\n        v = K.expand_dims(x, -1) + self.C\n        # print 'v.shape', v.shape\n        v = a * v\n        # print 'v.shape', v.shape\n        v = K.sum(v, axis=[1, 2])\n        # print 'v.shape', v.shape\n        v = K.permute_dimensions(v, pattern=[0, 2, 1])\n        # print 'v.shape', v.shape\n        #v.shape = None x K x D\n\n        # Normalize v (Intra Normalization)\n        v = v[:,0:self.num_clusters,:]\n        # print 'after ghosting v.shape', v.shape\n        v = K.l2_normalize( v, axis=-1 )\n        v = K.batch_flatten( v )\n        v = K.l2_normalize( v, axis=-1 )\n\n\n        # return [v, self.amap]\n        return v\n\n    def compute_output_shape( self, input_shape ):\n\n        # return [(input_shape[0], self.num_clusters*self.D ), (input_shape[0], input_shape[1], input_shape[2]) ]\n        return (input_shape[0], self.num_clusters*self.D )\n\n    def get_config( self ):\n        pass\n\n\n        # As suggested by: https://github.com/keras-team/keras/issues/4871#issuecomment-269731817\n        config = {'num_clusters': self.num_clusters, 'num_ghost_clusters': self.num_ghost_clusters}\n        base_config = super(GhostVLADLayer, self).get_config()\n        return dict(list(base_config.items()) + list(config.items()))\n\n\n#--------------------------- UTILS --------------------------------------------#\ndef open_json_file( fname ):\n    print 'Load JSON file: ', fname\n    jsonX = json.loads(open(fname).read())\n    return jsonX\n\n\ndef change_model_inputshape(model, new_input_shape=(None, 40, 40, 3), verbose=False):\n    \"\"\"\n    Given a model and new input shape it changes all the allocations.\n\n    Note: It uses custom_objects={'NetVLAD': NetVLADLayer}. If you have any other\n    custom-layer change the code here accordingly.\n    \"\"\"\n    print '[change_model_inputshape], new_input_shape=', new_input_shape\n    # replace input shape of first layer\n    model._layers[0].batch_input_shape = new_input_shape\n\n    # feel free to modify additional parameters of other layers, for example...\n    # model._layers[2].pool_size = (8, 8)\n    # model._layers[2].strides = (8, 8)\n\n    # rebuild model architecture by exporting and importing via json\n    new_model = keras.models.model_from_json(model.to_json(), custom_objects={'NetVLADLayer': NetVLADLayer, 'GhostVLADLayer':GhostVLADLayer}  )\n    # new_model.summary()\n\n    # copy weights from old model to new one\n    print 'copy weights from old model to new one....this usually takes upto 10 sec'\n    for layer in new_model.layers:\n        try:\n            if verbose:\n                print( 'transfer weights for layer.name='+layer.name )\n            layer.set_weights(model.get_layer(name=layer.name).get_weights())\n        except:\n            print '[Almost certainly FATAL] '\n            print 'If you see the warning like wieghts cannot be transfer, this is usually bad. In this case make sure the desired input dimensions and those in the modeljson file are compatible'\n            print(\"Could not transfer weights for layer {}\".format(layer.name))\n            quit()\n\n    # test new model on a random input image\n    # X = np.random.rand(new_input_shape[0], new_input_shape[1], new_input_shape[2], new_input_shape[3] )\n    # y_pred = new_model.predict(X)\n    # print('try predict with a random input_img with shape='+str(X.shape)+ str(y_pred) )\n\n    return new_model\n\n#--------------------------- END UTILS ----------------------------------------#\n"
  },
  {
    "path": "scripts/unittest/demo_superpoint.py",
    "content": "#!/usr/bin/env python\n#\n# %BANNER_BEGIN%\n# ---------------------------------------------------------------------\n# %COPYRIGHT_BEGIN%\n#\n#  Magic Leap, Inc. (\"COMPANY\") CONFIDENTIAL\n#\n#  Unpublished Copyright (c) 2018\n#  Magic Leap, Inc., All Rights Reserved.\n#\n# NOTICE:  All information contained herein is, and remains the property\n# of COMPANY. The intellectual and technical concepts contained herein\n# are proprietary to COMPANY and may be covered by U.S. and Foreign\n# Patents, patents in process, and are protected by trade secret or\n# copyright law.  Dissemination of this information or reproduction of\n# this material is strictly forbidden unless prior written permission is\n# obtained from COMPANY.  Access to the source code contained herein is\n# hereby forbidden to anyone except current COMPANY employees, managers\n# or contractors who have executed Confidentiality and Non-disclosure\n# agreements explicitly covering such access.\n#\n# The copyright notice above does not evidence any actual or intended\n# publication or disclosure  of  this source code, which includes\n# information that is confidential and/or proprietary, and is a trade\n# secret, of  COMPANY.   ANY REPRODUCTION, MODIFICATION, DISTRIBUTION,\n# PUBLIC  PERFORMANCE, OR PUBLIC DISPLAY OF OR THROUGH USE  OF THIS\n# SOURCE CODE  WITHOUT THE EXPRESS WRITTEN CONSENT OF COMPANY IS\n# STRICTLY PROHIBITED, AND IN VIOLATION OF APPLICABLE LAWS AND\n# INTERNATIONAL TREATIES.  THE RECEIPT OR POSSESSION OF  THIS SOURCE\n# CODE AND/OR RELATED INFORMATION DOES NOT CONVEY OR IMPLY ANY RIGHTS\n# TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, OR TO MANUFACTURE,\n# USE, OR SELL ANYTHING THAT IT  MAY DESCRIBE, IN WHOLE OR IN PART.\n#\n# %COPYRIGHT_END%\n# ----------------------------------------------------------------------\n# %AUTHORS_BEGIN%\n#\n#  Originating Authors: Daniel DeTone (ddetone)\n#                       Tomasz Malisiewicz (tmalisiewicz)\n#\n# %AUTHORS_END%\n# --------------------------------------------------------------------*/\n# %BANNER_END%\n\n\nimport argparse\nimport glob\nimport numpy as np\nimport os\nimport time\nimport code\n\nimport cv2\nimport torch\n\n# Stub to warn about opencv version.\nif int(cv2.__version__[0]) < 3: # pragma: no cover\n  print('Warning: OpenCV 3 is not installed')\n\n# Jet colormap for visualization.\nmyjet = np.array([[0.        , 0.        , 0.5       ],\n                  [0.        , 0.        , 0.99910873],\n                  [0.        , 0.37843137, 1.        ],\n                  [0.        , 0.83333333, 1.        ],\n                  [0.30044276, 1.        , 0.66729918],\n                  [0.66729918, 1.        , 0.30044276],\n                  [1.        , 0.90123457, 0.        ],\n                  [1.        , 0.48002905, 0.        ],\n                  [0.99910873, 0.07334786, 0.        ],\n                  [0.5       , 0.        , 0.        ]])\n\nclass SuperPointNet(torch.nn.Module):\n  \"\"\" Pytorch definition of SuperPoint Network. \"\"\"\n  def __init__(self):\n    super(SuperPointNet, self).__init__()\n    self.relu = torch.nn.ReLU(inplace=True)\n    self.pool = torch.nn.MaxPool2d(kernel_size=2, stride=2)\n    c1, c2, c3, c4, c5, d1 = 64, 64, 128, 128, 256, 256\n    # Shared Encoder.\n    self.conv1a = torch.nn.Conv2d(1, c1, kernel_size=3, stride=1, padding=1)\n    self.conv1b = torch.nn.Conv2d(c1, c1, kernel_size=3, stride=1, padding=1)\n    self.conv2a = torch.nn.Conv2d(c1, c2, kernel_size=3, stride=1, padding=1)\n    self.conv2b = torch.nn.Conv2d(c2, c2, kernel_size=3, stride=1, padding=1)\n    self.conv3a = torch.nn.Conv2d(c2, c3, kernel_size=3, stride=1, padding=1)\n    self.conv3b = torch.nn.Conv2d(c3, c3, kernel_size=3, stride=1, padding=1)\n    self.conv4a = torch.nn.Conv2d(c3, c4, kernel_size=3, stride=1, padding=1)\n    self.conv4b = torch.nn.Conv2d(c4, c4, kernel_size=3, stride=1, padding=1)\n    # Detector Head.\n    self.convPa = torch.nn.Conv2d(c4, c5, kernel_size=3, stride=1, padding=1)\n    self.convPb = torch.nn.Conv2d(c5, 65, kernel_size=1, stride=1, padding=0)\n    # Descriptor Head.\n    self.convDa = torch.nn.Conv2d(c4, c5, kernel_size=3, stride=1, padding=1)\n    self.convDb = torch.nn.Conv2d(c5, d1, kernel_size=1, stride=1, padding=0)\n\n  def forward(self, x):\n    \"\"\" Forward pass that jointly computes unprocessed point and descriptor\n    tensors.\n    Input\n      x: Image pytorch tensor shaped N x 1 x H x W.\n    Output\n      semi: Output point pytorch tensor shaped N x 65 x H/8 x W/8.\n      desc: Output descriptor pytorch tensor shaped N x 256 x H/8 x W/8.\n    \"\"\"\n    # Shared Encoder.\n    x = self.relu(self.conv1a(x))\n    x = self.relu(self.conv1b(x))\n    x = self.pool(x)\n    x = self.relu(self.conv2a(x))\n    x = self.relu(self.conv2b(x))\n    x = self.pool(x)\n    x = self.relu(self.conv3a(x))\n    x = self.relu(self.conv3b(x))\n    x = self.pool(x)\n    x = self.relu(self.conv4a(x))\n    x = self.relu(self.conv4b(x))\n    # Detector Head.\n    cPa = self.relu(self.convPa(x))\n    semi = self.convPb(cPa)\n    # Descriptor Head.\n    cDa = self.relu(self.convDa(x))\n    desc = self.convDb(cDa)\n    dn = torch.norm(desc, p=2, dim=1) # Compute the norm.\n    desc = desc.div(torch.unsqueeze(dn, 1)) # Divide by norm to normalize.\n    return semi, desc\n\n\nclass SuperPointFrontend(object):\n  \"\"\" Wrapper around pytorch net to help with pre and post image processing. \"\"\"\n  def __init__(self, weights_path, nms_dist, conf_thresh, nn_thresh,\n               cuda=False):\n    self.name = 'SuperPoint'\n    self.cuda = cuda\n    self.nms_dist = nms_dist\n    self.conf_thresh = conf_thresh\n    self.nn_thresh = nn_thresh # L2 descriptor distance for good match.\n    self.cell = 8 # Size of each output cell. Keep this fixed.\n    self.border_remove = 4 # Remove points this close to the border.\n\n    # Load the network in inference mode.\n    self.net = SuperPointNet()\n    if cuda:\n      # Train on GPU, deploy on GPU.\n      self.net.load_state_dict(torch.load(weights_path))\n      self.net = self.net.cuda()\n    else:\n      # Train on GPU, deploy on CPU.\n      self.net.load_state_dict(torch.load(weights_path,\n                               map_location=lambda storage, loc: storage))\n    self.net.eval()\n\n  def nms_fast(self, in_corners, H, W, dist_thresh):\n    \"\"\"\n    Run a faster approximate Non-Max-Suppression on numpy corners shaped:\n      3xN [x_i,y_i,conf_i]^T\n\n    Algo summary: Create a grid sized HxW. Assign each corner location a 1, rest\n    are zeros. Iterate through all the 1's and convert them either to -1 or 0.\n    Suppress points by setting nearby values to 0.\n\n    Grid Value Legend:\n    -1 : Kept.\n     0 : Empty or suppressed.\n     1 : To be processed (converted to either kept or supressed).\n\n    NOTE: The NMS first rounds points to integers, so NMS distance might not\n    be exactly dist_thresh. It also assumes points are within image boundaries.\n\n    Inputs\n      in_corners - 3xN numpy array with corners [x_i, y_i, confidence_i]^T.\n      H - Image height.\n      W - Image width.\n      dist_thresh - Distance to suppress, measured as an infinty norm distance.\n    Returns\n      nmsed_corners - 3xN numpy matrix with surviving corners.\n      nmsed_inds - N length numpy vector with surviving corner indices.\n    \"\"\"\n    grid = np.zeros((H, W)).astype(int) # Track NMS data.\n    inds = np.zeros((H, W)).astype(int) # Store indices of points.\n    # Sort by confidence and round to nearest int.\n    inds1 = np.argsort(-in_corners[2,:])\n    corners = in_corners[:,inds1]\n    rcorners = corners[:2,:].round().astype(int) # Rounded corners.\n    # Check for edge case of 0 or 1 corners.\n    if rcorners.shape[1] == 0:\n      return np.zeros((3,0)).astype(int), np.zeros(0).astype(int)\n    if rcorners.shape[1] == 1:\n      out = np.vstack((rcorners, in_corners[2])).reshape(3,1)\n      return out, np.zeros((1)).astype(int)\n    # Initialize the grid.\n    for i, rc in enumerate(rcorners.T):\n      grid[rcorners[1,i], rcorners[0,i]] = 1\n      inds[rcorners[1,i], rcorners[0,i]] = i\n    # Pad the border of the grid, so that we can NMS points near the border.\n    pad = dist_thresh\n    grid = np.pad(grid, ((pad,pad), (pad,pad)), mode='constant')\n    # Iterate through points, highest to lowest conf, suppress neighborhood.\n    count = 0\n    for i, rc in enumerate(rcorners.T):\n      # Account for top and left padding.\n      pt = (rc[0]+pad, rc[1]+pad)\n      if grid[pt[1], pt[0]] == 1: # If not yet suppressed.\n        grid[pt[1]-pad:pt[1]+pad+1, pt[0]-pad:pt[0]+pad+1] = 0\n        grid[pt[1], pt[0]] = -1\n        count += 1\n    # Get all surviving -1's and return sorted array of remaining corners.\n    keepy, keepx = np.where(grid==-1)\n    keepy, keepx = keepy - pad, keepx - pad\n    inds_keep = inds[keepy, keepx]\n    out = corners[:, inds_keep]\n    values = out[-1, :]\n    inds2 = np.argsort(-values)\n    out = out[:, inds2]\n    out_inds = inds1[inds_keep[inds2]]\n    return out, out_inds\n\n  def run(self, img):\n    \"\"\" Process a numpy image to extract points and descriptors.\n    Input\n      img - HxW numpy float32 input image in range [0,1].\n    Output\n      corners - 3xN numpy array with corners [x_i, y_i, confidence_i]^T.\n      desc - 256xN numpy array of corresponding unit normalized descriptors.\n      heatmap - HxW numpy heatmap in range [0,1] of point confidences.\n      \"\"\"\n    assert img.ndim == 2, 'Image must be grayscale.'\n    assert img.dtype == np.float32, 'Image must be float32.'\n    H, W = img.shape[0], img.shape[1]\n    inp = img.copy()\n    inp = (inp.reshape(1, H, W))\n    inp = torch.from_numpy(inp)\n    inp = torch.autograd.Variable(inp).view(1, 1, H, W)\n    if self.cuda:\n      inp = inp.cuda()\n    # Forward pass of network.\n    outs = self.net.forward(inp)\n    semi, coarse_desc = outs[0], outs[1]\n    # Convert pytorch -> numpy.\n    semi = semi.data.cpu().numpy().squeeze()\n    # --- Process points.\n    dense = np.exp(semi) # Softmax.\n    dense = dense / (np.sum(dense, axis=0)+.00001) # Should sum to 1.\n    # Remove dustbin.\n    nodust = dense[:-1, :, :]\n    # Reshape to get full resolution heatmap.\n    Hc = int(H / self.cell)\n    Wc = int(W / self.cell)\n    nodust = nodust.transpose(1, 2, 0)\n    heatmap = np.reshape(nodust, [Hc, Wc, self.cell, self.cell])\n    heatmap = np.transpose(heatmap, [0, 2, 1, 3])\n    heatmap = np.reshape(heatmap, [Hc*self.cell, Wc*self.cell])\n    xs, ys = np.where(heatmap >= self.conf_thresh) # Confidence threshold.\n    if len(xs) == 0:\n      return np.zeros((3, 0)), None, None\n    pts = np.zeros((3, len(xs))) # Populate point data sized 3xN.\n    pts[0, :] = ys\n    pts[1, :] = xs\n    pts[2, :] = heatmap[xs, ys]\n    pts, _ = self.nms_fast(pts, H, W, dist_thresh=self.nms_dist) # Apply NMS.\n    inds = np.argsort(pts[2,:])\n    pts = pts[:,inds[::-1]] # Sort by confidence.\n    # Remove points along border.\n    bord = self.border_remove\n    toremoveW = np.logical_or(pts[0, :] < bord, pts[0, :] >= (W-bord))\n    toremoveH = np.logical_or(pts[1, :] < bord, pts[1, :] >= (H-bord))\n    toremove = np.logical_or(toremoveW, toremoveH)\n    pts = pts[:, ~toremove]\n    # --- Process descriptor.\n    D = coarse_desc.shape[1]\n    if pts.shape[1] == 0:\n      desc = np.zeros((D, 0))\n    else:\n      # Interpolate into descriptor map using 2D point locations.\n      samp_pts = torch.from_numpy(pts[:2, :].copy())\n      samp_pts[0, :] = (samp_pts[0, :] / (float(W)/2.)) - 1.\n      samp_pts[1, :] = (samp_pts[1, :] / (float(H)/2.)) - 1.\n      samp_pts = samp_pts.transpose(0, 1).contiguous()\n      samp_pts = samp_pts.view(1, 1, -1, 2)\n      samp_pts = samp_pts.float()\n      if self.cuda:\n        samp_pts = samp_pts.cuda()\n      desc = torch.nn.functional.grid_sample(coarse_desc, samp_pts)\n      desc = desc.data.cpu().numpy().reshape(D, -1)\n      desc /= np.linalg.norm(desc, axis=0)[np.newaxis, :]\n    return pts, desc, heatmap\n\n\nclass PointTracker(object):\n  \"\"\" Class to manage a fixed memory of points and descriptors that enables\n  sparse optical flow point tracking.\n\n  Internally, the tracker stores a 'tracks' matrix sized M x (2+L), of M\n  tracks with maximum length L, where each row corresponds to:\n  row_m = [track_id_m, avg_desc_score_m, point_id_0_m, ..., point_id_L-1_m].\n  \"\"\"\n\n  def __init__(self, max_length, nn_thresh):\n    if max_length < 2:\n      raise ValueError('max_length must be greater than or equal to 2.')\n    self.maxl = max_length\n    self.nn_thresh = nn_thresh\n    self.all_pts = []\n    for n in range(self.maxl):\n      self.all_pts.append(np.zeros((2, 0)))\n    self.last_desc = None\n    self.tracks = np.zeros((0, self.maxl+2))\n    self.track_count = 0\n    self.max_score = 9999\n\n  def nn_match_two_way(self, desc1, desc2, nn_thresh):\n    \"\"\"\n    Performs two-way nearest neighbor matching of two sets of descriptors, such\n    that the NN match from descriptor A->B must equal the NN match from B->A.\n\n    Inputs:\n      desc1 - NxM numpy matrix of N corresponding M-dimensional descriptors.\n      desc2 - NxM numpy matrix of N corresponding M-dimensional descriptors.\n      nn_thresh - Optional descriptor distance below which is a good match.\n\n    Returns:\n      matches - 3xL numpy array, of L matches, where L <= N and each column i is\n                a match of two descriptors, d_i in image 1 and d_j' in image 2:\n                [d_i index, d_j' index, match_score]^T\n    \"\"\"\n    assert desc1.shape[0] == desc2.shape[0]\n    if desc1.shape[1] == 0 or desc2.shape[1] == 0:\n      return np.zeros((3, 0))\n    if nn_thresh < 0.0:\n      raise ValueError('\\'nn_thresh\\' should be non-negative')\n    # Compute L2 distance. Easy since vectors are unit normalized.\n    dmat = np.dot(desc1.T, desc2)\n    dmat = np.sqrt(2-2*np.clip(dmat, -1, 1))\n    # Get NN indices and scores.\n    idx = np.argmin(dmat, axis=1)\n    scores = dmat[np.arange(dmat.shape[0]), idx]\n    # Threshold the NN matches.\n    keep = scores < nn_thresh\n    # Check if nearest neighbor goes both directions and keep those.\n    idx2 = np.argmin(dmat, axis=0)\n    keep_bi = np.arange(len(idx)) == idx2[idx]\n    keep = np.logical_and(keep, keep_bi)\n    idx = idx[keep]\n    scores = scores[keep]\n    # Get the surviving point indices.\n    m_idx1 = np.arange(desc1.shape[1])[keep]\n    m_idx2 = idx\n    # Populate the final 3xN match data structure.\n    matches = np.zeros((3, int(keep.sum())))\n    matches[0, :] = m_idx1\n    matches[1, :] = m_idx2\n    matches[2, :] = scores\n    return matches\n\n  def get_offsets(self):\n    \"\"\" Iterate through list of points and accumulate an offset value. Used to\n    index the global point IDs into the list of points.\n\n    Returns\n      offsets - N length array with integer offset locations.\n    \"\"\"\n    # Compute id offsets.\n    offsets = []\n    offsets.append(0)\n    for i in range(len(self.all_pts)-1): # Skip last camera size, not needed.\n      offsets.append(self.all_pts[i].shape[1])\n    offsets = np.array(offsets)\n    offsets = np.cumsum(offsets)\n    return offsets\n\n  def update(self, pts, desc):\n    \"\"\" Add a new set of point and descriptor observations to the tracker.\n\n    Inputs\n      pts - 3xN numpy array of 2D point observations.\n      desc - DxN numpy array of corresponding D dimensional descriptors.\n    \"\"\"\n    if pts is None or desc is None:\n      print('PointTracker: Warning, no points were added to tracker.')\n      return\n    assert pts.shape[1] == desc.shape[1]\n    # Initialize last_desc.\n    if self.last_desc is None:\n      self.last_desc = np.zeros((desc.shape[0], 0))\n    # Remove oldest points, store its size to update ids later.\n    remove_size = self.all_pts[0].shape[1]\n    self.all_pts.pop(0)\n    self.all_pts.append(pts)\n    # Remove oldest point in track.\n    self.tracks = np.delete(self.tracks, 2, axis=1)\n    # Update track offsets.\n    for i in range(2, self.tracks.shape[1]):\n      self.tracks[:, i] -= remove_size\n    self.tracks[:, 2:][self.tracks[:, 2:] < -1] = -1\n    offsets = self.get_offsets()\n    # Add a new -1 column.\n    self.tracks = np.hstack((self.tracks, -1*np.ones((self.tracks.shape[0], 1))))\n    # Try to append to existing tracks.\n    matched = np.zeros((pts.shape[1])).astype(bool)\n    matches = self.nn_match_two_way(self.last_desc, desc, self.nn_thresh)\n    for match in matches.T:\n      # Add a new point to it's matched track.\n      id1 = int(match[0]) + offsets[-2]\n      id2 = int(match[1]) + offsets[-1]\n      found = np.argwhere(self.tracks[:, -2] == id1)\n      if found.shape[0] > 0:\n        matched[int(match[1])] = True\n        row = int(found)\n        self.tracks[row, -1] = id2\n        if self.tracks[row, 1] == self.max_score:\n          # Initialize track score.\n          self.tracks[row, 1] = match[2]\n        else:\n          # Update track score with running average.\n          # NOTE(dd): this running average can contain scores from old matches\n          #           not contained in last max_length track points.\n          track_len = (self.tracks[row, 2:] != -1).sum() - 1.\n          frac = 1. / float(track_len)\n          self.tracks[row, 1] = (1.-frac)*self.tracks[row, 1] + frac*match[2]\n    # Add unmatched tracks.\n    new_ids = np.arange(pts.shape[1]) + offsets[-1]\n    new_ids = new_ids[~matched]\n    new_tracks = -1*np.ones((new_ids.shape[0], self.maxl + 2))\n    new_tracks[:, -1] = new_ids\n    new_num = new_ids.shape[0]\n    new_trackids = self.track_count + np.arange(new_num)\n    new_tracks[:, 0] = new_trackids\n    new_tracks[:, 1] = self.max_score*np.ones(new_ids.shape[0])\n    self.tracks = np.vstack((self.tracks, new_tracks))\n    self.track_count += new_num # Update the track count.\n    # Remove empty tracks.\n    keep_rows = np.any(self.tracks[:, 2:] >= 0, axis=1)\n    self.tracks = self.tracks[keep_rows, :]\n    # Store the last descriptors.\n    self.last_desc = desc.copy()\n    return\n\n  def get_tracks(self, min_length):\n    \"\"\" Retrieve point tracks of a given minimum length.\n    Input\n      min_length - integer >= 1 with minimum track length\n    Output\n      returned_tracks - M x (2+L) sized matrix storing track indices, where\n        M is the number of tracks and L is the maximum track length.\n    \"\"\"\n    if min_length < 1:\n      raise ValueError('\\'min_length\\' too small.')\n    valid = np.ones((self.tracks.shape[0])).astype(bool)\n    good_len = np.sum(self.tracks[:, 2:] != -1, axis=1) >= min_length\n    # Remove tracks which do not have an observation in most recent frame.\n    not_headless = (self.tracks[:, -1] != -1)\n    keepers = np.logical_and.reduce((valid, good_len, not_headless))\n    returned_tracks = self.tracks[keepers, :].copy()\n    return returned_tracks\n\n  def draw_tracks(self, out, tracks):\n    \"\"\" Visualize tracks all overlayed on a single image.\n    Inputs\n      out - numpy uint8 image sized HxWx3 upon which tracks are overlayed.\n      tracks - M x (2+L) sized matrix storing track info.\n    \"\"\"\n    # Store the number of points per camera.\n    pts_mem = self.all_pts\n    N = len(pts_mem) # Number of cameras/images.\n    # Get offset ids needed to reference into pts_mem.\n    offsets = self.get_offsets()\n    # Width of track and point circles to be drawn.\n    stroke = 1\n    # Iterate through each track and draw it.\n    for track in tracks:\n      clr = myjet[int(np.clip(np.floor(track[1]*10), 0, 9)), :]*255\n      for i in range(N-1):\n        if track[i+2] == -1 or track[i+3] == -1:\n          continue\n        offset1 = offsets[i]\n        offset2 = offsets[i+1]\n        idx1 = int(track[i+2]-offset1)\n        idx2 = int(track[i+3]-offset2)\n        pt1 = pts_mem[i][:2, idx1]\n        pt2 = pts_mem[i+1][:2, idx2]\n        p1 = (int(round(pt1[0])), int(round(pt1[1])))\n        p2 = (int(round(pt2[0])), int(round(pt2[1])))\n        cv2.line(out, p1, p2, clr, thickness=stroke, lineType=16)\n        # Draw end points of each track.\n        if i == N-2:\n          clr2 = (255, 0, 0)\n          cv2.circle(out, p2, stroke, clr2, -1, lineType=16)\n\nclass VideoStreamer(object):\n  \"\"\" Class to help process image streams. Three types of possible inputs:\"\n    1.) USB Webcam.\n    2.) A directory of images (files in directory matching 'img_glob').\n    3.) A video file, such as an .mp4 or .avi file.\n  \"\"\"\n  def __init__(self, basedir, camid, height, width, skip, img_glob):\n    self.cap = []\n    self.camera = False\n    self.video_file = False\n    self.listing = []\n    self.sizer = [height, width]\n    self.i = 0\n    self.skip = skip\n    self.maxlen = 1000000\n    # If the \"basedir\" string is the word camera, then use a webcam.\n    if basedir == \"camera/\" or basedir == \"camera\":\n      print('==> Processing Webcam Input.')\n      self.cap = cv2.VideoCapture(camid)\n      self.listing = range(0, self.maxlen)\n      self.camera = True\n    else:\n      # Try to open as a video.\n      self.cap = cv2.VideoCapture(basedir)\n      lastbit = basedir[-4:len(basedir)]\n      if (type(self.cap) == list or not self.cap.isOpened()) and (lastbit == '.mp4'):\n        raise IOError('Cannot open movie file')\n      elif type(self.cap) != list and self.cap.isOpened() and (lastbit != '.txt'):\n        print('==> Processing Video Input.')\n        num_frames = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))\n        self.listing = range(0, num_frames)\n        self.listing = self.listing[::self.skip]\n        self.camera = True\n        self.video_file = True\n        self.maxlen = len(self.listing)\n      else:\n        print('==> Processing Image Directory Input.')\n        search = os.path.join(basedir, img_glob)\n        self.listing = glob.glob(search)\n        self.listing.sort()\n        self.listing = self.listing[::self.skip]\n        self.maxlen = len(self.listing)\n        if self.maxlen == 0:\n          raise IOError('No images were found (maybe bad \\'--img_glob\\' parameter?)')\n\n  def read_image(self, impath, img_size):\n    \"\"\" Read image as grayscale and resize to img_size.\n    Inputs\n      impath: Path to input image.\n      img_size: (W, H) tuple specifying resize size.\n    Returns\n      grayim: float32 numpy array sized H x W with values in range [0, 1].\n    \"\"\"\n    grayim = cv2.imread(impath, 0)\n    if grayim is None:\n      raise Exception('Error reading image %s' % impath)\n    # Image is resized via opencv.\n    interp = cv2.INTER_AREA\n    grayim = cv2.resize(grayim, (img_size[1], img_size[0]), interpolation=interp)\n    grayim = (grayim.astype('float32') / 255.)\n    return grayim\n\n  def next_frame(self):\n    \"\"\" Return the next frame, and increment internal counter.\n    Returns\n       image: Next H x W image.\n       status: True or False depending whether image was loaded.\n    \"\"\"\n    if self.i == self.maxlen:\n      return (None, False)\n    if self.camera:\n      ret, input_image = self.cap.read()\n      if ret is False:\n        print('VideoStreamer: Cannot get image from camera (maybe bad --camid?)')\n        return (None, False)\n      if self.video_file:\n        self.cap.set(cv2.CAP_PROP_POS_FRAMES, self.listing[self.i])\n      input_image = cv2.resize(input_image, (self.sizer[1], self.sizer[0]),\n                               interpolation=cv2.INTER_AREA)\n      input_image = cv2.cvtColor(input_image, cv2.COLOR_RGB2GRAY)\n      input_image = input_image.astype('float')/255.0\n    else:\n      image_file = self.listing[self.i]\n      input_image = self.read_image(image_file, self.sizer)\n    # Increment internal counter.\n    self.i = self.i + 1\n    input_image = input_image.astype('float32')\n    return (input_image, True)\n\n\nif __name__ == '__main__':\n\n  # Parse command line arguments.\n  parser = argparse.ArgumentParser(description='PyTorch SuperPoint Demo.')\n  parser.add_argument('input', type=str, default='',\n      help='Image directory or movie file or \"camera\" (for webcam).')\n  parser.add_argument('--weights_path', type=str, default='superpoint_v1.pth',\n      help='Path to pretrained weights file (default: superpoint_v1.pth).')\n  parser.add_argument('--img_glob', type=str, default='*.png',\n      help='Glob match if directory of images is specified (default: \\'*.png\\').')\n  parser.add_argument('--skip', type=int, default=1,\n      help='Images to skip if input is movie or directory (default: 1).')\n  parser.add_argument('--show_extra', action='store_true',\n      help='Show extra debug outputs (default: False).')\n  parser.add_argument('--H', type=int, default=120,\n      help='Input image height (default: 120).')\n  parser.add_argument('--W', type=int, default=160,\n      help='Input image width (default:160).')\n  parser.add_argument('--display_scale', type=int, default=2,\n      help='Factor to scale output visualization (default: 2).')\n  parser.add_argument('--min_length', type=int, default=2,\n      help='Minimum length of point tracks (default: 2).')\n  parser.add_argument('--max_length', type=int, default=5,\n      help='Maximum length of point tracks (default: 5).')\n  parser.add_argument('--nms_dist', type=int, default=4,\n      help='Non Maximum Suppression (NMS) distance (default: 4).')\n  parser.add_argument('--conf_thresh', type=float, default=0.015,\n      help='Detector confidence threshold (default: 0.015).')\n  parser.add_argument('--nn_thresh', type=float, default=0.7,\n      help='Descriptor matching threshold (default: 0.7).')\n  parser.add_argument('--camid', type=int, default=0,\n      help='OpenCV webcam video capture ID, usually 0 or 1 (default: 0).')\n  parser.add_argument('--waitkey', type=int, default=1,\n      help='OpenCV waitkey time in ms (default: 1).')\n  parser.add_argument('--cuda', action='store_true',\n      help='Use cuda GPU to speed up network processing speed (default: False)')\n  parser.add_argument('--no_display', action='store_true',\n      help='Do not display images to screen. Useful if running remotely (default: False).')\n  parser.add_argument('--write', action='store_true',\n      help='Save output frames to a directory (default: False)')\n  parser.add_argument('--write_dir', type=str, default='tracker_outputs/',\n      help='Directory where to write output frames (default: tracker_outputs/).')\n  opt = parser.parse_args()\n  print(opt)\n  # code.interact( local=locals() )\n  # This class helps load input images from different sources.\n  vs = VideoStreamer(opt.input, opt.camid, opt.H, opt.W, opt.skip, opt.img_glob)\n\n  print('==> Loading pre-trained network.')\n  # This class runs the SuperPoint network and processes its outputs.\n  fe = SuperPointFrontend(weights_path=opt.weights_path,\n                          nms_dist=opt.nms_dist,\n                          conf_thresh=opt.conf_thresh,\n                          nn_thresh=opt.nn_thresh,\n                          cuda=opt.cuda)\n  print('==> Successfully loaded pre-trained network.')\n\n  # This class helps merge consecutive point matches into tracks.\n  tracker = PointTracker(opt.max_length, nn_thresh=fe.nn_thresh)\n\n  # Create a window to display the demo.\n  if not opt.no_display:\n    win = 'SuperPoint Tracker'\n    cv2.namedWindow(win)\n  else:\n    print('Skipping visualization, will not show a GUI.')\n\n  # Font parameters for visualizaton.\n  font = cv2.FONT_HERSHEY_DUPLEX\n  font_clr = (255, 255, 255)\n  font_pt = (4, 12)\n  font_sc = 0.4\n\n  # Create output directory if desired.\n  if opt.write:\n    print('==> Will write outputs to %s' % opt.write_dir)\n    if not os.path.exists(opt.write_dir):\n      os.makedirs(opt.write_dir)\n\n  print('==> Running Demo.')\n  while True:\n\n    start = time.time()\n\n    # Get a new image.\n    img, status = vs.next_frame()\n    if status is False:\n      break\n\n    # Get points and descriptors.\n    start1 = time.time()\n    pts, desc, heatmap = fe.run(img)\n    # import code\n    # code.interact( local=locals() )\n    end1 = time.time()\n\n    # Add points and descriptors to the tracker.\n    tracker.update(pts, desc)\n\n    # Get tracks for points which were match successfully across all frames.\n    tracks = tracker.get_tracks(opt.min_length)\n\n    # Primary output - Show point tracks overlayed on top of input image.\n    out1 = (np.dstack((img, img, img)) * 255.).astype('uint8')\n    tracks[:, 1] /= float(fe.nn_thresh) # Normalize track scores to [0,1].\n    tracker.draw_tracks(out1, tracks)\n    if opt.show_extra:\n      cv2.putText(out1, 'Point Tracks', font_pt, font, font_sc, font_clr, lineType=16)\n\n    # Extra output -- Show current point detections.\n    out2 = (np.dstack((img, img, img)) * 255.).astype('uint8')\n    for pt in pts.T:\n      pt1 = (int(round(pt[0])), int(round(pt[1])))\n      cv2.circle(out2, pt1, 1, (0, 255, 0), -1, lineType=16)\n    cv2.putText(out2, 'Raw Point Detections', font_pt, font, font_sc, font_clr, lineType=16)\n\n    # Extra output -- Show the point confidence heatmap.\n    if heatmap is not None:\n      min_conf = 0.001\n      heatmap[heatmap < min_conf] = min_conf\n      heatmap = -np.log(heatmap)\n      heatmap = (heatmap - heatmap.min()) / (heatmap.max() - heatmap.min() + .00001)\n      out3 = myjet[np.round(np.clip(heatmap*10, 0, 9)).astype('int'), :]\n      out3 = (out3*255).astype('uint8')\n    else:\n      out3 = np.zeros_like(out2)\n    cv2.putText(out3, 'Raw Point Confidences', font_pt, font, font_sc, font_clr, lineType=16)\n\n    # Resize final output.\n    if opt.show_extra:\n      out = np.hstack((out1, out2, out3))\n      out = cv2.resize(out, (3*opt.display_scale*opt.W, opt.display_scale*opt.H))\n    else:\n      out = cv2.resize(out1, (opt.display_scale*opt.W, opt.display_scale*opt.H))\n\n    # Display visualization image to screen.\n    if not opt.no_display:\n      cv2.imshow(win, out)\n      key = cv2.waitKey(opt.waitkey) & 0xFF\n      if key == ord('q'):\n        print('Quitting, \\'q\\' pressed.')\n        break\n\n    # Optionally write images to disk.\n    if opt.write:\n      out_file = os.path.join(opt.write_dir, 'frame_%05d.png' % vs.i)\n      print('Writing image to %s' % out_file)\n      cv2.imwrite(out_file, out)\n\n    end = time.time()\n    net_t = (1./ float(end1 - start))\n    total_t = (1./ float(end - start))\n    if opt.show_extra:\n      print('Processed image %d (net+post_process: %.2f FPS, total: %.2f FPS).'\\\n            % (vs.i, net_t, total_t))\n\n  # Close any remaining windows.\n  cv2.destroyAllWindows()\n\n  print('==> Finshed Demo.')\n"
  },
  {
    "path": "scripts/unittest/rtry_superpoint.py",
    "content": "#!/usr/bin/env python\n\nimport numpy as np\nimport code\nimport cv2\nimport time\n\nimport json\n\nimport torch\n# Stub to warn about opencv version.\nif int(cv2.__version__[0]) < 3: # pragma: no cover\n  print('Warning: OpenCV 3 is not installed')\n\nfrom demo_superpoint import *\n\nclass KSuperPointExp:\n    def __init__(self):\n        #------------------------------------------------------------------------\n        # Setup Neural Net\n        weights_path='superpoint_v1.pth'\n        nms_dist=4\n        conf_thresh=0.015\n        nn_thresh=0.7\n        cuda=True\n        print('==> Loading pre-trained network.')\n        # This class runs the SuperPoint network and processes its outputs.\n        self.fe = SuperPointFrontend(weights_path=weights_path,\n                             nms_dist=nms_dist,\n                             conf_thresh=conf_thresh,\n                             nn_thresh=nn_thresh,\n                             cuda=cuda)\n        print('==> Successfully loaded pre-trained network.')\n\n\n        #-----------------------------------------------------------------------\n        # Tracker - NN comparison for desc\n        max_length = 5 # default as in the demo file\n        self.tracker = PointTracker(max_length, nn_thresh=self.fe.nn_thresh)\n\n\n    def plot_pts( self, xcanvas, pts ):\n        # pts:  [x_i, y_i, confidence_i]. 3xN\n        for i in range( pts.shape[1] ):\n            cv2.circle( xcanvas, (int(pts[0,i]), int(pts[1,i]) ), 1, (0,0,255), -1 )\n\n        return xcanvas\n\n    def plot_point_sets( self, im0, pts0, im1, pts1 ):\n        # pts:  [x_i, y_i, confidence_i]. 3xN\n        # try:\n        xcanvas = np.concatenate( (im0, im1), axis=1 )\n        if pts0.shape[0] == 0:\n            return xcanvas\n        # except:\n            # code.interact( local=locals() )\n        assert( pts0.shape[1] == pts1.shape[1] )\n        assert( pts0.shape[0] == 3 or pts0.shape[0] == 2 )\n        assert( pts1.shape[0] == 3 or pts1.shape[0] == 2 )\n\n\n        for i in range( pts0.shape[1] ):\n            pt0 = (int(pts0[0,i]), int(pts0[1,i]) )\n            pt1 = (int(pts1[0,i] + im0.shape[1] ), int(pts1[1,i]) )\n            cv2.circle( xcanvas, pt0, 1, (0,255,0), -1)\n            cv2.circle( xcanvas, pt1, 1, (0,255,0), -1)\n            cv2.line( xcanvas, pt0, pt1, (0,255,0) )\n\n        return xcanvas\n\n\n\n    def read_image(self, impath, img_size):\n        \"\"\" Read image as grayscale and resize to img_size.\n        Inputs\n          impath: Path to input image.\n          img_size: (W, H) tuple specifying resize size.\n        Returns\n          grayim: float32 numpy array sized H x W with values in range [0, 1].\n        \"\"\"\n        grayim = cv2.imread(impath, 0)\n        if grayim is None:\n          raise Exception('Error reading image %s' % impath)\n        # Image is resized via opencv.\n        interp = cv2.INTER_AREA\n        grayim = cv2.resize(grayim, (img_size[1], img_size[0]), interpolation=interp)\n        grayim = (grayim.astype('float32') / 255.)\n        return grayim\n\n\n    def get_descriptor( self, image ):\n        \"\"\" Given an image returns the keypoints and descriptors.\n            Input\n              img - HxW numpy float32 input image in range [0,1].\n            Output\n              corners - 3xN numpy array with corners [x_i, y_i, confidence_i]^T.\n              desc - 256xN numpy array of corresponding unit normalized descriptors.\n              heatmap - HxW numpy heatmap in range [0,1] of point confidences.\n        \"\"\"\n        start1 = time.time()\n        pts, desc, heatmap = self.fe.run(image)\n        print 'superpoint computed in %4.2fms' %(1000. * (time.time() - start1))\n        return pts, desc, heatmap\n\n\n    def match_image_pair( self, fname0, fname1 ):\n        \"\"\" Given two image file names, read the two images (as per SuperPoint's requirement).\n        Computes descriptors and then does NN comparison.\n        \"\"\"\n\n        ksp_image0 = ksp.read_image(fname0, [240,320] )\n        ksp_image1 = ksp.read_image(fname1, [240,320] )\n        image0 = cv2.imread( fname0 )\n        image1 = cv2.imread( fname1 )\n\n        pts0, desc0, heatmap0 = ksp.get_descriptor( ksp_image0 )\n        pts1, desc1, heatmap1 = ksp.get_descriptor( ksp_image1 )\n        print 'len(pts0)=%d, len(pts1)=%d, desc0.shape=%s, desc1.shape=%s' %(pts0.shape[1], pts1.shape[1], str(desc0.shape), str(desc1.shape) )\n\n        start_matching = time.time()\n        matches = self.tracker.nn_match_two_way( desc0, desc1, self.fe.nn_thresh  )\n        sel_pts0 = np.array([ pts0[:,i] for i in matches[0,:].astype('int32') ]).transpose()\n        sel_pts1 = np.array([ pts1[:,i] for i in matches[1,:].astype('int32') ]).transpose()\n        print 'matching dones in %4.2fms' %(1000. * (time.time() - start_matching) )\n        print 'matches=%d' %( matches.shape[1] )\n        if matches.shape[1] == 0:\n            print 'NO MATCHES'\n            return None, None\n\n        # Plotting\n        # xcanvas0 = self.plot_pts(image0.copy(), pts0 )\n        # xcanvas1 = self.plot_pts(image1.copy(), pts1 )\n        # xcanvas_ = self.plot_point_sets( xcanvas0, sel_pts0, xcanvas1, sel_pts1 )\n        # xcanvas_ = self.plot_point_sets( image0.copy(), sel_pts0, image1.copy(), sel_pts1 )\n        # cv2.imshow( 'pts0', xcanvas0 )\n        # cv2.imshow( 'pts1', xcanvas1 )\n        # cv2.imshow( 'joint', xcanvas_ )\n\n        return sel_pts0[0:2,:].transpose() , sel_pts1[0:2,:].transpose()\n        # code.interact( local=locals() )\n\n\nif __name__ == '__main__':\n    BASE = '/Bulk_Data/_tmp_cerebro/bb4_multiple_loops_in_lab/'\n\n\n    #\n    # Open Log File\n    #\n    LOG_FILE_NAME = BASE+'/log.json'\n    print 'Open file: ', LOG_FILE_NAME\n    with open(LOG_FILE_NAME) as data_file:\n        data = json.load(data_file)\n\n\n    #\n    # Init Keras Model\n    ksp = KSuperPointExp()\n\n    if True:\n        i = 534\n        im = cv2.imread( BASE+'%d.jpg' %(i) )\n\n        im_float = cv2.cvtColor( cv2.resize(im, (0,0), fx=0.5, fy=0.5), cv2.COLOR_BGR2GRAY ).astype('float32') / 255.\n        print 'im_float.shape=', im_float.shape, '\\tdtype=', im_float.dtype\n\n        sup_pts, sup_desc, sup_heatmap = ksp.get_descriptor( im_float )\n        cv2.imshow( str(i), ksp.plot_pts( im, sup_pts*2 ) )\n\n    if True:\n        i = 1638\n        im = cv2.imread( BASE+'%d.jpg' %(i) )\n\n        im_float = cv2.cvtColor( cv2.resize(im, (0,0), fx=0.5, fy=0.5), cv2.COLOR_BGR2GRAY ).astype('float32') / 255.\n        print 'im_float.shape=', im_float.shape, '\\tdtype=', im_float.dtype\n\n        sup_pts, sup_desc, sup_heatmap = ksp.get_descriptor( im_float )\n        cv2.imshow( str(i), ksp.plot_pts( im, sup_pts*2 ) )\n\n    cv2.waitKey(0)\n\n\nif __name__ == \"__m1ain__\":\n    BASE = '/Bulk_Data/_tmp_cerebro/bb4_multiple_loops_in_lab/'\n\n\n    #\n    # Open Log File\n    #\n    LOG_FILE_NAME = BASE+'/log.json'\n    print 'Open file: ', LOG_FILE_NAME\n    with open(LOG_FILE_NAME) as data_file:\n        data = json.load(data_file)\n\n\n    #\n    # Init Keras Model\n    ksp = KSuperPointExp()\n\n\n\n    # Loops over all images and precomputes their netvlad vector\n    for i in range( len(data['DataNodes']) ):\n        a = data['DataNodes'][i]['isKeyFrame']\n        b = data['DataNodes'][i]['isImageAvailable']\n        c = data['DataNodes'][i]['isPoseAvailable']\n        d = data['DataNodes'][i]['isPtCldAvailable']\n\n\n        if not ( a==1 and b==1 and c==1 and d==1 ): #only process keyframes which have pose and ptcld info\n            continue\n\n        im = cv2.imread( BASE+'%d.jpg' %(i) )\n\n        im_float = cv2.cvtColor( cv2.resize(im, (0,0), fx=0.5, fy=0.5), cv2.COLOR_BGR2GRAY ).astype('float32') / 255.\n        print 'im_float.shape=', im_float.shape, '\\tdtype=', im_float.dtype\n\n        start_time = time.time()\n        sup_pts, sup_desc, sup_heatmap = ksp.get_descriptor( im_float )\n        print '---', i , '\\n',\n        print 'Done in %4.4fms' %( 1000. * (time.time() - start_time ) )\n\n        cv2.imshow( 'im_superpoints', ksp.plot_pts( im, sup_pts*2 ) )\n        cv2.imshow( 'im', im )\n        key = cv2.waitKey(10)\n        if key == ord( 'q' ):\n            break\n"
  },
  {
    "path": "scripts/whole_image_desc_compute_client.py",
    "content": "#!/usr/bin/env python\n\n# Sample Client to call the `whole_image_descriptor_compute_server`\n\nfrom cerebro.srv import *\nimport rospy\nimport numpy as np\nimport cv2\n\nfrom cv_bridge import CvBridge, CvBridgeError\nfrom sensor_msgs.msg import Image\n\nrospy.wait_for_service( 'whole_image_descriptor_compute' )\ntry:\n    res = rospy.ServiceProxy( 'whole_image_descriptor_compute', WholeImageDescriptorCompute )\n\n    # X = np.zeros( (100, 100), dtype=np.uint8 )\n    X = cv2.resize( cv2.imread( '/app/lena.jpg' ), (752,480) )\n    i = CvBridge().cv2_to_imgmsg( X )\n    u = res( i, 23  )\n    print 'received: ', u\nexcept rospy.ServiceException, e:\n    print 'failed', e\n"
  },
  {
    "path": "scripts/whole_image_desc_compute_server.py",
    "content": "#!/usr/bin/env python\n\nfrom cerebro.srv import *\nimport rospy\n\nfrom cv_bridge import CvBridge, CvBridgeError\nimport cv2\nimport numpy as np\nimport code\nimport time\nimport os\nimport scipy.io\nimport hashlib\n\nimport keras\nfrom keras_helpers import *\nfrom predict_utils import *\nfrom TerminalColors import bcolors\ntcol = bcolors()\n\nimport rospkg\nTHIS_PKG_BASE_PATH = rospkg.RosPack().get_path('cerebro')\n\nfrom numpy.random import seed\nseed(42)\n\nclass SampleGPUComputer:\n    def __init__(self):\n        self.model = keras.applications.vgg16.VGG16( weights=None)\n        self.model._make_predict_function()\n        self.model.summary()\n\n\n\n    def handle_req( self, req ):\n        print 'Handle request '\n\n\n        # print req\n        cv_image = CvBridge().imgmsg_to_cv2( req.ima )\n        print 'cv_image.shape', cv_image.shape\n        # cv2.imshow( 'cv_image from server', cv_image )\n        # cv2.waitKey(0)\n\n        #\n        # compute descriptor\n        #\n        # code.interact( local=locals() )\n        im = cv2.resize( cv_image, (224,224) )\n        self.model._make_predict_function()\n        preds = self.model.predict( np.expand_dims(im,0).astype('float32') )\n        print preds\n\n\n        #\n        # Return the descriptor in the message\n        #\n        result = WholeImageDescriptorComputeResponse()\n        result.desc = [ cv_image.shape[0], cv_image.shape[1] ]\n        return result\n\nclass ReljaNetVLAD:\n    def __init__(self, im_rows=600, im_cols=960, im_chnls=3):\n        ## Build net\n        from keras.backend.tensorflow_backend import set_session\n        import tensorflow as tf\n        tf.set_random_seed(42)\n        config = tf.ConfigProto()\n        config.gpu_options.per_process_gpu_memory_fraction = 0.1\n        # config.gpu_options.visible_device_list = \"0\"\n        set_session(tf.Session(config=config))\n\n        self.im_rows = int(im_rows)\n        self.im_cols = int(im_cols)\n        self.im_chnls = int(im_chnls)\n\n\n        input_img = keras.layers.Input( batch_shape=(1,self.im_rows, self.im_cols, self.im_chnls ) )\n        cnn = make_from_vgg16( input_img, weights=None, layer_name='block5_pool' )\n        out, out_amap = NetVLADLayer(num_clusters = 64)( cnn )\n        model = keras.models.Model( inputs=input_img, outputs=out )\n\n        DATA_DIR = '/app_learning/cartwheel_train/relja_matlab_weight.dump/'\n        print 'Relja Data Dir: ', DATA_DIR\n        model.load_weights( DATA_DIR+'/matlab_model.keras' )\n        WPCA_M = scipy.io.loadmat( DATA_DIR+'/WPCA_1.mat' )['the_mat'] # 1x1x32768x4096\n        WPCA_b = scipy.io.loadmat( DATA_DIR+'/WPCA_2.mat' )['the_mat'] # 4096x1\n        WPCA_M = WPCA_M[0,0]          # 32768x4096\n        WPCA_b = np.transpose(WPCA_b) #1x4096\n\n\n        self.model = model\n        self.WPCA_M = WPCA_M\n        self.WPCA_b = WPCA_b\n        self.model_type = 'relja_matlab_model'\n\n\n\n        # Doing this is a hack to force keras to allocate GPU memory. Don't comment this,\n        tmp_zer = np.zeros( (1,self.im_rows,self.im_cols,self.im_chnls), dtype='float32' )\n        tmp_zer_out = self.model.predict( tmp_zer )\n        tmp_zer_out = np.matmul( tmp_zer_out, self.WPCA_M ) + self.WPCA_b\n        tmp_zer_out /= np.linalg.norm( tmp_zer_out )\n        print 'model input.shape=', tmp_zer.shape, '\\toutput.shape=', tmp_zer_out.shape\n        print 'model_type=', self.model_type\n\n        print '-----'\n        print '\\tinput_image.shape=', tmp_zer.shape\n        print '\\toutput.shape=', tmp_zer_out.shape\n        print '\\tminmax=', np.min( tmp_zer_out ), np.max( tmp_zer_out )\n        print '\\tnorm=', np.linalg.norm( tmp_zer_out )\n        print '\\tdtype=', tmp_zer_out.dtype\n        print '-----'\n\n\n    def handle_req( self, req ):\n        ## Get Image out of req\n        cv_image = CvBridge().imgmsg_to_cv2( req.ima )\n        print '[Handle Request] cv_image.shape', cv_image.shape, '\\ta=', req.a, '\\tt=', req.ima.header.stamp\n\n\n        assert (cv_image.shape[0] == self.im_rows and\n                cv_image.shape[1] == self.im_cols and\n                cv_image.shape[2] == self.im_chnls),\\\n                \"\\n[whole_image_descriptor_compute_server] Input shape of the image \\\n                does not match with the allocated GPU memory. Expecting an input image of \\\n                size %dx%dx%d, but received : %s\" %(self.im_rows, self.im_cols, self.im_chnls, str(cv_image.shape) )\n\n        # cv2.imshow( 'whole_image_descriptor_compute_server:imshow', cv_image.astype('uint8') )\n        # cv2.waitKey(10)\n        # cv2.imwrite( '/app/tmp/%s.jpg' %( str(req.ima.header.stamp) ), cv_image )\n\n        ## Compute Descriptor\n        start_time = time.time()\n\n        # Normalize image\n        cv_image_rgb = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)\n        avg_image = [122.6778, 116.6522, 103.9997]\n        cv_image_rgb[:,:,0]= cv_image_rgb[:,:,0] - avg_image[0]\n        cv_image_rgb[:,:,1]= cv_image_rgb[:,:,1] - avg_image[1]\n        cv_image_rgb[:,:,2]= cv_image_rgb[:,:,2] - avg_image[2]\n\n\n        # predict\n        u = self.model.predict( np.expand_dims( cv_image_rgb.astype('float32'), 0 ) )\n\n        # WPCA\n        u = np.matmul( u, self.WPCA_M ) + self.WPCA_b\n        u /= np.linalg.norm( u )\n\n        print 'Descriptor Computed in %4.4fms' %( 1000. *(time.time() - start_time) ),\n        print '\\tdesc.shape=', u.shape,\n        print '\\tinput_image.shape=', cv_image.shape,\n        print '\\tminmax=', np.min( u ), np.max( u ),\n        print '\\tmodel_type=', self.model_type,\n        print '\\tdtype=', cv_image.dtype\n\n\n\n        ## Populate output message\n        result = WholeImageDescriptorComputeResponse()\n        # result.desc = [ cv_image.shape[0], cv_image.shape[1] ]\n        result.desc = u[0,:]\n        result.model_type = self.model_type\n        return result\n\n\n\n\nclass NetVLADImageDescriptor:\n    def __init__(self, im_rows=600, im_cols=960, im_chnls=3):\n        ## Build net\n        from keras.backend.tensorflow_backend import set_session\n        import tensorflow as tf\n        tf.set_random_seed(42)\n        config = tf.ConfigProto()\n        config.gpu_options.per_process_gpu_memory_fraction = 0.1\n        # config.gpu_options.visible_device_list = \"0\"\n        set_session(tf.Session(config=config))\n\n        # Blackbox 4\n        # self.im_rows = 512\n        # self.im_cols = 640\n        # self.im_chnls = 3\n\n        # point grey\n        # self.im_rows = 600\n        # self.im_cols = 960\n        # self.im_chnls = 3\n\n        # EuroC\n        # self.im_rows = 480\n        # self.im_cols = 752\n        # self.im_chnls = 3\n\n        self.im_rows = int(im_rows)\n        self.im_cols = int(im_cols)\n        self.im_chnls = int(im_chnls)\n\n\n        #----- @ INPUT LAYER\n        input_img = keras.layers.Input( shape=(self.im_rows, self.im_cols, self.im_chnls) )\n\n        #----- @ CNN\n        # cnn = make_from_vgg16( input_img, weights=None, layer_name='block5_pool' )\n        cnn = make_from_mobilenet( input_img, weights=None, layer_name='conv_pw_7_relu' )\n\n        #----- @ DOWN-SAMPLE LAYER (OPTINAL)\n        if False: #Downsample last layer (Reduce nChannels of the output.)\n            cnn_dwn = keras.layers.Conv2D( 256, (1,1), padding='same', activation='relu' )( cnn )\n            cnn_dwn = keras.layers.normalization.BatchNormalization()( cnn_dwn )\n            cnn_dwn = keras.layers.Conv2D( 64, (1,1), padding='same', activation='relu' )( cnn_dwn )\n            cnn_dwn = keras.layers.normalization.BatchNormalization()( cnn_dwn )\n            cnn = cnn_dwn\n\n        #----- @ NetVLADLayer\n        # out, out_amap = NetVLADLayer(num_clusters = 16)( cnn )\n        out = NetVLADLayer(num_clusters = 16)( cnn )\n\n        model = keras.models.Model( inputs=input_img, outputs=out )\n        model.summary()\n        # `model_visual_fname` as None will disable writing the image file.\n        model_visual_fname = None\n        # model_visual_fname = '/app/core.png'\n        if model_visual_fname is not None:\n            print 'Writing Model Visual to: ', model_visual_fname\n            keras.utils.plot_model( model, to_file=model_visual_fname, show_shapes=True )\n\n\n        #----- @ Load Model Weights\n        #TODO Read from config file the path of keras model\n        # model_file = '/app/catkin_ws/src/cerebro/scripts/keras.models/core_model.keras'\n        # model_type = 'test_keras_model'\n\n        # model_file = '/app_learning/cartwheel_train/models.keras/vgg16/block5_pool_k16_tripletloss2/core_model.keras'\n        # model_type = 'block5_pool_k16_tripletloss2'\n\n\n        # model_file = '/app_learning/cartwheel_train/models.keras/mobilenet_conv7_quash_chnls_allpairloss/core_model.keras'\n        # model_type = 'mobilenet_conv7_quash_chnls_allpairloss'\n\n        #model_file = '/app_learning/cartwheel_train/models.keras/mobilenet_conv7_allpairloss/core_model.keras'\n        #model_type = 'mobilenet_conv7_allpairloss'\n\n        # model_file = '/app_learning/cartwheel_train/models.keras/mobilenet_new/pw13_quash_chnls_k16_allpairloss/core_model.1800.keras'\n        # model_type = 'pw13_quash_chnls_k16_allpairloss'\n\n        # model_file = '/app_learning/cartwheel_train/models.keras/vgg16_new/block5_pool_k16_tripletloss2/core_model.keras'\n        # model_type = 'block5_pool_k16_tripletloss2'\n\n        # model_file = '/app_learning/cartwheel_train/models.keras/mobilenet_new/pw13_quash_chnls_k16_allpairloss/core_model.800.keras'\n        # model_type = 'pw13_quash_chnls_k16_allpairloss'\n\n        model_file = THIS_PKG_BASE_PATH+'/scripts/keras.models/mobilenet_conv7_allpairloss.keras'\n        model_type = 'mobilenet_conv7_allpairloss'\n\n        print 'model_file: ', model_file\n        model.load_weights( model_file )\n\n\n\n        # Write model as json to file, for info.\n        # if False:\n        #     model.summary()\n        #     keras.utils.plot_model( model, to_file='/tmp/xcore.png', show_shapes=True )\n        #     with open('/tmp/xmodel.json', 'w') as outfile:\n        #         json.dump(model.to_json(), outfile, indent=4 )\n\n        self.model = model\n        self.model_type = model_type\n        # ! Done...!\n\n        # Doing this is a hack to force keras to allocate GPU memory. Don't comment this,\n        tmp_zer = np.zeros( (1,self.im_rows,self.im_cols,self.im_chnls), dtype='float32' )\n        tmp_zer_out = self.model.predict( tmp_zer )\n        print 'model input.shape=', tmp_zer.shape, '\\toutput.shape=', tmp_zer_out.shape\n        print 'model_type=', self.model_type\n\n        print '-----'\n        print '\\tinput_image.shape=', tmp_zer.shape\n        print '\\toutput.shape=', tmp_zer_out.shape\n        print '\\tminmax=', np.min( tmp_zer_out ), np.max( tmp_zer_out )\n        print '\\tdtype=', tmp_zer_out.dtype\n        print '-----'\n\n\n\n    def handle_req( self, req ):\n        ## Get Image out of req\n        cv_image = CvBridge().imgmsg_to_cv2( req.ima )\n        print '[Handle Request] cv_image.shape', cv_image.shape, '\\ta=', req.a, '\\tt=', req.ima.header.stamp\n\n        if len(cv_image.shape) == 2:\n            cv_image = np.expand_dims( cv_image, -1 );\n\n        assert (cv_image.shape[0] == self.im_rows and\n                cv_image.shape[1] == self.im_cols and\n                cv_image.shape[2] == self.im_chnls),\\\n                \"\\n[whole_image_descriptor_compute_server] Input shape of the image \\\n                does not match with the allocated GPU memory. Expecting an input image of \\\n                size %dx%dx%d, but received : %s\" %(self.im_rows, self.im_cols, self.im_chnls, str(cv_image.shape) )\n\n        # cv2.imshow( 'whole_image_descriptor_compute_server:imshow', cv_image.astype('uint8') )\n        # cv2.waitKey(10)\n        # cv2.imwrite( '/app/tmp/%s.jpg' %( str(req.ima.header.stamp) ), cv_image )\n\n        ## Compute Descriptor\n        start_time = time.time()\n        u = self.model.predict( np.expand_dims( cv_image.astype('float32'), 0 ) )\n        print 'Descriptor Computed in %4.4fms' %( 1000. *(time.time() - start_time) ),\n        print '\\tdesc.shape=', u.shape,\n        print '\\tinput_image.shape=', cv_image.shape,\n        print '\\tminmax=', np.min( u ), np.max( u ),\n        print '\\tnorm=', np.linalg.norm(u[0]),\n        print '\\tmodel_type=', self.model_type,\n        print '\\tdtype=', cv_image.dtype\n\n\n\n        ## Populate output message\n        result = WholeImageDescriptorComputeResponse()\n        # result.desc = [ cv_image.shape[0], cv_image.shape[1] ]\n        result.desc = u[0,:]\n        result.model_type = self.model_type\n        return result\n\n\nclass JSONModelImageDescriptor:\n    \"\"\"\n    This class loads the net structure from the json file, model.json and\n    weights from core_model.??.keras. In the argument `kerasmodel_file`\n    you need to specify the core_model.??.keras full path (keras model file).\n    The model.json need to exist in that folder.\n    \"\"\"\n    def __init__(self, kerasmodel_file, im_rows=600, im_cols=960, im_chnls=3):\n        ## Build net\n        from keras.backend.tensorflow_backend import set_session\n        import tensorflow as tf\n        tf.set_random_seed(42)\n        config = tf.ConfigProto()\n        config.gpu_options.per_process_gpu_memory_fraction = 0.2\n        # config.gpu_options.visible_device_list = \"0\"\n        set_session(tf.Session(config=config))\n\n        # Blackbox 4\n        # self.im_rows = 512\n        # self.im_cols = 640\n        # self.im_chnls = 3\n\n        # point grey\n        # self.im_rows = 600\n        # self.im_cols = 960\n        # self.im_chnls = 3\n\n        # EuroC\n        # self.im_rows = 480\n        # self.im_cols = 752\n        # self.im_chnls = 3\n\n        self.im_rows = int(im_rows)\n        self.im_cols = int(im_cols)\n        self.im_chnls = int(im_chnls)\n\n        LOG_DIR = '/'.join( kerasmodel_file.split('/')[0:-1] )\n        print '+++++++++++++++++++++++++++++++++++++++++++++++++++++++'\n        print '++++++++++ (JSONModelImageDescriptor) LOG_DIR=', LOG_DIR\n        print '++++++++++ im_rows=', im_rows, ' im_cols=', im_cols, ' im_chnls=', im_chnls\n        print '+++++++++++++++++++++++++++++++++++++++++++++++++++++++'\n        model_type = LOG_DIR.split('/')[-1]\n        # code.interact( local=locals() )\n\n        assert os.path.isdir( LOG_DIR ), \"The LOG_DIR doesnot exist, or there is a permission issue. LOG_DIR=\"+LOG_DIR\n        assert os.path.isfile(  LOG_DIR+'/model.json'  ), \"model.json does not exist in LOG_DIR=\"+LOG_DIR+'. This file is needed to load the network architecture from json format. This file should have been created when you learned the model using script in github.com/mpkuse/cartwheel_train'\n\n\n\n        #----- @ Load Model Structure from JSON\n        # Load JSON formatted model\n        json_string = open_json_file( LOG_DIR+'/model.json' )\n        # print '======================='\n        # pprint.pprint( json_string, indent=4 )\n        # print '======================='\n        model = keras.models.model_from_json(str(json_string),  custom_objects={'NetVLADLayer': NetVLADLayer, 'GhostVLADLayer':GhostVLADLayer} )\n        old_input_shape = model._layers[0].input_shape\n\n        model._layers[0]\n        print 'OLD MODEL: ', 'input_shape=', str(old_input_shape)\n        model.summary()\n        model_visual_fname = None\n        # model_visual_fname = '/app/core.png'\n        if model_visual_fname is not None:\n            print 'Writing Model Visual to: ', model_visual_fname\n            keras.utils.plot_model( model, to_file=model_visual_fname, show_shapes=True )\n\n        #----- @ Load Weights\n        assert os.path.isfile( kerasmodel_file ), 'The model weights file doesnot exists or there is a permission issue.'+\"kerasmodel_file=\"+kerasmodel_file\n        model_fname = kerasmodel_file\n        print tcol.OKGREEN, 'Load model: ', model_fname, tcol.ENDC\n        model.load_weights(  model_fname )\n\n\n        # Replace Input Layer\n        new_model = change_model_inputshape( model, new_input_shape=(1,self.im_rows,self.im_cols,self.im_chnls) )\n        new_input_shape = new_model._layers[0].input_shape\n        print 'OLD MODEL: ', 'input_shape=', str(old_input_shape)\n        print 'NEW MODEL: input_shape=', str(new_input_shape)\n\n        self.model = new_model\n        self.model_type = model_type\n\n\n        # Doing this is a hack to force keras to allocate GPU memory. Don't comment this,\n        tmp_zer = np.zeros( (1,self.im_rows,self.im_cols,self.im_chnls), dtype='float32' )\n        tmp_zer_out = self.model.predict( tmp_zer )\n        print 'model input.shape=', tmp_zer.shape, '\\toutput.shape=', tmp_zer_out.shape\n        print 'model_type=', self.model_type\n\n        print '-----'\n        print '\\tinput_image.shape=', tmp_zer.shape\n        print '\\toutput.shape=', tmp_zer_out.shape\n        print '\\tminmax=', np.min( tmp_zer_out ), np.max( tmp_zer_out )\n        print '\\tnorm=', np.linalg.norm( tmp_zer_out )\n        print '\\tdtype=', tmp_zer_out.dtype\n        print '-----'\n\n\n\n    def handle_req( self, req ):\n        \"\"\" The received image from CV bridge has to be [0,255]. In function makes it to\n        intensity range [-0.5 to 0.5]\n        \"\"\"\n        ## Get Image out of req\n        cv_image = CvBridge().imgmsg_to_cv2( req.ima )\n        print '[Handle Request] cv_image.shape', cv_image.shape, '\\ta=', req.a, '\\tt=', req.ima.header.stamp\n        if len(cv_image.shape)==2:\n            # print 'Input dimensions are NxM but I am expecting it to be NxMxC, so np.expand_dims'\n            cv_image = np.expand_dims( cv_image, -1 )\n        elif len( cv_image.shape )==3:\n            pass\n        else:\n            assert( False )\n\n\n\n\n        assert (cv_image.shape[0] == self.im_rows and\n                cv_image.shape[1] == self.im_cols and\n                cv_image.shape[2] == self.im_chnls),\\\n                \"\\n[whole_image_descriptor_compute_server] Input shape of the image \\\n                does not match with the allocated GPU memory. Expecting an input image of \\\n                size %dx%dx%d, but received : %s\" %(self.im_rows, self.im_cols, self.im_chnls, str(cv_image.shape) )\n\n        # cv2.imshow( 'whole_image_descriptor_compute_server:imshow', cv_image.astype('uint8') )\n        # cv2.waitKey(10)\n        # cv2.imwrite( '/app/tmp/%s.jpg' %( str(req.ima.header.stamp) ), cv_image )\n\n        ## Compute Descriptor\n        start_time = time.time()\n        # i__image = np.expand_dims( cv_image.astype('float32'), 0 )\n        # i__image = (np.expand_dims( cv_image.astype('float32'), 0 ) - 128.)/255. [-0.5,0.5]\n        i__image = (np.expand_dims( cv_image.astype('float32'), 0 ) - 128.)*2.0/255. #[-1,1]\n\n        u = self.model.predict( i__image )\n\n        print tcol.HEADER, 'Descriptor Computed in %4.4fms' %( 1000. *(time.time() - start_time) ), tcol.ENDC\n        print '\\tinput_image.shape=', cv_image.shape,\n        print '\\tinput_image dtype=', cv_image.dtype\n        print tcol.OKBLUE, '\\tinput image (to neuralnet) minmax=', np.min( i__image ), np.max( i__image ), tcol.ENDC\n        print '\\tdesc.shape=', u.shape,\n        print '\\tdesc minmax=', np.min( u ), np.max( u ),\n        print '\\tnorm=', np.linalg.norm(u[0])\n        print '\\tmodel_type=', self.model_type\n\n\n\n        ## Populate output message\n        result = WholeImageDescriptorComputeResponse()\n        # result.desc = [ cv_image.shape[0], cv_image.shape[1] ]\n        result.desc = u[0,:]\n        result.model_type = self.model_type\n        return result\n\n\n\nclass HDF5ModelImageDescriptor:\n    \"\"\"\n    This class loads the net structure from the .h5 file. This file contains\n    the model weights as well as architecture details.\n    In the argument `kerasmodel_file`\n    you need to specify the core_model.??.keras full path (keras model file).\n\n    \"\"\"\n    def __init__(self, kerasmodel_file, im_rows=600, im_cols=960, im_chnls=3):\n        ## Build net\n        from keras.backend.tensorflow_backend import set_session\n        import tensorflow as tf\n        tf.set_random_seed(42)\n        config = tf.ConfigProto()\n        config.gpu_options.per_process_gpu_memory_fraction = 0.2\n        # config.gpu_options.visible_device_list = \"0\"\n        set_session(tf.Session(config=config))\n        keras.backend.tensorflow_backend.set_learning_phase(0)\n\n        self.request_count = 0\n\n        # Blackbox 4\n        # self.im_rows = 512\n        # self.im_cols = 640\n        # self.im_chnls = 3\n\n        # point grey\n        # self.im_rows = 600\n        # self.im_cols = 960\n        # self.im_chnls = 3\n\n        # EuroC\n        # self.im_rows = 480\n        # self.im_cols = 752\n        # self.im_chnls = 3\n\n        self.im_rows = int(im_rows)\n        self.im_cols = int(im_cols)\n        self.im_chnls = int(im_chnls)\n\n        LOG_DIR = '/'.join( kerasmodel_file.split('/')[0:-1] )\n        print '+++++++++++++++++++++++++++++++++++++++++++++++++++++++'\n        print '++++++++++ (HDF5ModelImageDescriptor) LOG_DIR=', LOG_DIR\n        print '++++++++++ im_rows=', im_rows, ' im_cols=', im_cols, ' im_chnls=', im_chnls\n        print '+++++++++++++++++++++++++++++++++++++++++++++++++++++++'\n        model_type = LOG_DIR.split('/')[-1]\n\n\n        assert os.path.isdir( LOG_DIR ), \"The LOG_DIR doesnot exist, or there is a permission issue. LOG_DIR=\"+LOG_DIR\n\n        # See if cached file exists, if yes load it else do as usual\n        digest = '/tmp/'+str(hashlib.sha256(kerasmodel_file).hexdigest())+'.h5'\n        print 'Check for exisitance of file', digest\n        if  os.path.isfile(digest):\n            print 'The cached file, ', digest, ' seems to exists, load that file'\n            print tcol.OKGREEN, 'Load model (from cache): ', digest, tcol.ENDC\n            model = keras.models.load_model(  digest, custom_objects={'NetVLADLayer': NetVLADLayer, 'GhostVLADLayer':GhostVLADLayer} )\n            model.summary()\n            print tcol.OKGREEN, 'Load model (from cache): ', digest, tcol.ENDC\n\n            self.model = model\n            self.model_type = model_type\n\n        else:\n            # Load from HDF5\n            assert os.path.isfile( kerasmodel_file ), 'The model weights file doesnot exists or there is a permission issue.'+\"kerasmodel_file=\"+kerasmodel_file\n            model_fname = kerasmodel_file\n            print tcol.OKGREEN, 'Load model: ', model_fname, tcol.ENDC\n            model = keras.models.load_model(  model_fname, custom_objects={'NetVLADLayer': NetVLADLayer, 'GhostVLADLayer':GhostVLADLayer} )\n            old_input_shape = model._layers[0].input_shape\n            print 'OLD MODEL: ', 'input_shape=', str(old_input_shape)\n            model.summary()\n            model_visual_fname = None\n            if model_visual_fname is not None:\n                print 'Writing Model Visual to: ', model_visual_fname\n                keras.utils.plot_model( model, to_file=model_visual_fname, show_shapes=True )\n\n\n\n            # Replace Input Layer\n            new_model = change_model_inputshape( model, new_input_shape=(1,self.im_rows,self.im_cols,self.im_chnls) )\n            new_input_shape = new_model._layers[0].input_shape\n            print 'OLD MODEL: ', 'input_shape=', str(old_input_shape)\n            print 'NEW MODEL: input_shape=', str(new_input_shape)\n\n            self.model = new_model\n            self.model_type = model_type\n\n            # Write to cache\n            if True:\n                digest = '/tmp/'+str(hashlib.sha256(kerasmodel_file).hexdigest())+'.h5'\n                print tcol.WARNING, '}}}}}}}}\\nWrite out cache in '+digest+'\\n}}}}}}}}', tcol.ENDC\n                self.model.save( digest )\n\n\n        # Doing this is a hack to force keras to allocate GPU memory. Don't comment this,\n        tmp_zer = np.zeros( (1,self.im_rows,self.im_cols,self.im_chnls), dtype='float32' )\n        tmp_zer_out = self.model.predict( tmp_zer )\n        print 'model input.shape=', tmp_zer.shape, '\\toutput.shape=', tmp_zer_out.shape\n        print 'model_type=', self.model_type\n\n        print '-----'\n        print '\\tinput_image.shape=', tmp_zer.shape\n        print '\\toutput.shape=', tmp_zer_out.shape\n        print '\\tminmax=', np.min( tmp_zer_out ), np.max( tmp_zer_out )\n        print '\\tnorm=', np.linalg.norm( tmp_zer_out )\n        print '\\tdtype=', tmp_zer_out.dtype\n        print '-----'\n\n\n\n    def handle_req( self, req ):\n        \"\"\" The received image from CV bridge has to be [0,255]. In function makes it to\n        intensity range [-0.5 to 0.5]\n        \"\"\"\n        ## Get Image out of req\n        cv_image = CvBridge().imgmsg_to_cv2( req.ima )\n        print '[HDF5ModelImageDescriptor Handle Request#%d] cv_image.shape' %(self.request_count), cv_image.shape, '\\ta=', req.a, '\\tt=', req.ima.header.stamp\n        if len(cv_image.shape)==2:\n            # print 'Input dimensions are NxM but I am expecting it to be NxMxC, so np.expand_dims'\n            cv_image = np.expand_dims( cv_image, -1 )\n        elif len( cv_image.shape )==3:\n            pass\n        else:\n            assert( False )\n\n\n\n\n        assert (cv_image.shape[0] == self.im_rows and\n                cv_image.shape[1] == self.im_cols and\n                cv_image.shape[2] == self.im_chnls),\\\n                \"\\n[whole_image_descriptor_compute_server] Input shape of the image \\\n                does not match with the allocated GPU memory. Expecting an input image of \\\n                size %dx%dx%d, but received : %s\" %(self.im_rows, self.im_cols, self.im_chnls, str(cv_image.shape) )\n\n        # cv2.imshow( 'whole_image_descriptor_compute_server:imshow', cv_image.astype('uint8') )\n        # cv2.waitKey(10)\n        # cv2.imwrite( '/app/tmp/%s.jpg' %( str(req.ima.header.stamp) ), cv_image )\n\n        ## Compute Descriptor\n        start_time = time.time()\n        # i__image = np.expand_dims( cv_image.astype('float32'), 0 )\n        # i__image = (np.expand_dims( cv_image.astype('float32'), 0 ) - 128.)/255. [-0.5,0.5]\n        i__image = (np.expand_dims( cv_image.astype('float32'), 0 ) - 128.)*2.0/255. #[-1,1]\n\n        u = self.model.predict( i__image )\n\n        print tcol.HEADER, 'Descriptor Computed in %4.4fms' %( 1000. *(time.time() - start_time) ), tcol.ENDC\n        print '\\tinput_image.shape=', cv_image.shape,\n        print '\\tinput_image dtype=', cv_image.dtype\n        print tcol.OKBLUE, '\\tinput image (to neuralnet) minmax=', np.min( i__image ), np.max( i__image ), tcol.ENDC\n        print '\\tdesc.shape=', u.shape,\n        print '\\tdesc minmax=', np.min( u ), np.max( u ),\n        print '\\tnorm=', np.linalg.norm(u[0])\n        print '\\tmodel_type=', self.model_type\n        self.request_count += 1\n\n\n\n        ## Populate output message\n        result = WholeImageDescriptorComputeResponse()\n        # result.desc = [ cv_image.shape[0], cv_image.shape[1] ]\n        result.desc = u[0,:]\n        result.model_type = self.model_type\n        return result\n\n\n\nrospy.init_node( 'whole_image_descriptor_compute_server' )\n\n##\n## Load the config file and read image row, col\n##\nfs_image_width = -1\nfs_image_height = -1\nfs_image_chnls = 1\n\nif True: # read from param `config_file`\n    if not rospy.has_param( '~config_file'):\n        print 'FATAL...cannot find param ~config_file. This is needed to determine size of the input image to allocate GPU memory. If you do not specify the config_file, you need to atleast specify the nrows, ncols, nchnls'\n        rospy.logerr( '[whole_image_descriptor_compute_server]FATAL...cannot find param ~config_file. This is needed to determine size of the input image to allocate GPU memory. If you do not specify the config_file, you need to atleast specify the nrows, ncols, nchnls' )\n\n        if ( rospy.has_param( '~nrows') and rospy.has_param( '~ncols') ):\n            print tcol.OKGREEN, 'However, you seem to have set the parameters nrows and ncols, so will read those.', tcol.ENDC\n        else:\n            quit()\n        # quit only if you cannot see nrows and ncols\n\n    else:\n        config_file = rospy.get_param('~config_file')\n        print '++++++++\\n++++++++ config_file: ', config_file\n        if not os.path.isfile(config_file):\n            print 'FATAL...cannot find config_file: ', config_file\n            rospy.logerr( '[whole_image_descriptor_compute_server]FATAL...cannot find config_file: '+ config_file )\n            quit()\n\n\n        print '++++++++ READ opencv-yaml file: ', config_file\n        fs = cv2.FileStorage(config_file, cv2.FILE_STORAGE_READ)\n        fs_image_width = int(  fs.getNode( \"image_width\" ).real() )\n        fs_image_height = int( fs.getNode( \"image_height\" ).real() )\n        print '++++++++ opencv-yaml:: image_width=', fs_image_width, '   image_height=', fs_image_height\n        print '++++++++'\n\n\n##\n## Load nrows and ncols directly as config\n##\nif True:  # read from param `nrows` and `ncols`\n    if fs_image_width < 0 :\n        if ( not rospy.has_param( '~nrows') or not rospy.has_param( '~ncols') or not rospy.has_param( '~nchnls') ):\n            print 'FATAL...cannot find param either of ~nrows, ~ncols, ~nchnls. This is needed to determine size of the input image to allocate GPU memory'\n            rospy.logerr( '[whole_image_descriptor_compute_server] FATAL...cannot find param either of ~nrows, ~ncols, nchnls. This is needed to determine size of the input image to allocate GPU memory' )\n            quit()\n        else:\n            fs_image_height = rospy.get_param('~nrows')\n            fs_image_width = rospy.get_param('~ncols')\n            fs_image_chnls = rospy.get_param('~nchnls')\n\n            print '~~~~~~~~~~~~~~~~'\n            print '~nrows = ', fs_image_height, '\\t~ncols = ', fs_image_width, '\\t~nchnls = ', fs_image_chnls\n            print '~~~~~~~~~~~~~~~~'\n\n\n##\n## Load Channels\n##\nif True:\n    if not rospy.has_param( '~nchnls' ):\n        rospy.logerr( \"[whole_image_descriptor_compute_server] FATAL....cannot file cmd param nchnls.\")\n        quit()\n    else:\n        fs_image_chnls = rospy.get_param('~nchnls')\n\n##\n## Start Server\n##\n\n\n# Something wrong with jsonfile loading or with change_model_inputshape().\nif True:\n    # kerasmodel_file = '/models.keras/Apr2019/gray_conv6_K16Ghost1__centeredinput/core_model.%d.keras' %(500)\n    if rospy.has_param( '~kerasmodel_file'):\n        kerasmodel_file = rospy.get_param('~kerasmodel_file')\n    else:\n        print tcol.ERROR, 'FATAL...missing specification of model file. You need to specify ~kerasmodel_file', tcol.ENDC\n        quit()\n    # gpu_netvlad = JSONModelImageDescriptor( kerasmodel_file=kerasmodel_file, im_rows=fs_image_height, im_cols=fs_image_width, im_chnls=fs_image_chnls )\n    gpu_netvlad = HDF5ModelImageDescriptor( kerasmodel_file=kerasmodel_file, im_rows=fs_image_height, im_cols=fs_image_width, im_chnls=fs_image_chnls )\nelse:\n    #gpu_s = SampleGPUComputer()\n    gpu_netvlad = NetVLADImageDescriptor( im_rows=fs_image_height, im_cols=fs_image_width )\n    # gpu_netvlad = ReljaNetVLAD( im_rows=fs_image_height, im_cols=fs_image_width )\n\n\ns = rospy.Service( 'whole_image_descriptor_compute', WholeImageDescriptorCompute, gpu_netvlad.handle_req  )\nprint 'whole_image_descriptor_compute_server is running'\n\n\n# image = cv2.resize(  cv2.imread( '/app/lena.jpg'), (224,224) )\n# print gpu_s.model.predict( np.expand_dims(image,0) )\n\n\n\nrospy.spin()\n"
  },
  {
    "path": "src/Cerebro.cpp",
    "content": "#include \"Cerebro.h\"\n\n//-------------------------------------------------------------//\n//--- Setup Cerebro\n//-------------------------------------------------------------//\nCerebro::Cerebro( ros::NodeHandle& nh )\n{\n    b_run_thread = false;\n    b_descriptor_computer_thread = false;\n    this->nh = nh;\n\n    connected_to_descriptor_server = false;\n    descriptor_size_available = false;\n}\n\nvoid Cerebro::setDataManager( DataManager* dataManager )\n{\n    this->dataManager = dataManager;\n    m_dataManager_available = true;\n}\n\nvoid Cerebro::setPublishers( const string base_topic_name )\n{\n    string pub_loopedge_topic = base_topic_name+\"/loopedge\";\n    ROS_INFO( \"[Cerebro::setPublishers] Publish <cerebro::LoopEdge> %s\", pub_loopedge_topic.c_str() );\n    pub_loopedge = nh.advertise<cerebro::LoopEdge>( pub_loopedge_topic, 1000 );\n\n    m_pub_available = true;\n}\n\n//-------------------------------------------------------------//\n//--- END Setup Cerebro\n//-------------------------------------------------------------//\n\n\n//------------------------------------------------------------------//\n// per Keyframe Descriptor Computation\n//     Can have more threads to compute other aspects for keyframes\n//     like texts, objects visible etc. TODO\n//------------------------------------------------------------------//\n\n#define __Cerebro__descriptor_computer_thread( msg ) ;\n// #define __Cerebro__descriptor_computer_thread( msg ) msg\n\n#define __Cerebro__descriptor_computer_thread__imp( msg ) ;\n// #define __Cerebro__descriptor_computer_thread__imp( msg ) msg;\nvoid Cerebro::descriptor_computer_thread()\n{\n    assert( m_dataManager_available && \"You need to set the DataManager in class Cerebro before execution of the run() thread can begin. You can set the dataManager by call to Cerebro::setDataManager()\\n\");\n    assert( b_descriptor_computer_thread && \"You need to call descriptor_computer_thread_enable() before spawning the thread\\n\" );\n\n    //---------\n    // Send a zeros image to the server just to know the descriptor size\n    int nrows=-1, ncols=-1, desc_size=-1;\n    int nChannels = 1; //< ***This needs to be set correctly depending on the model_type. If you need error messages from server about channels size, you need to change this. ****\n    //----------\n\n    // Service Call\n    // Sample Code : https://github.com/mpkuse/cerebro/blob/master/src/unittest/unittest_rosservice_client.cpp\n    connected_to_descriptor_server = false;\n    descriptor_size_available = false;\n    int n_sec_wait_for_connection = 71;\n    cout << TermColor::iYELLOW() << \"[Cerebro::descriptor_computer_thread]Attempt connecting to ros-service for \" << n_sec_wait_for_connection << \" sec (will give up after that)\\n\" << TermColor::RESET();\n    ros::ServiceClient client = nh.serviceClient<cerebro::WholeImageDescriptorCompute>( \"/whole_image_descriptor_compute\" );\n    client.waitForExistence( ros::Duration(n_sec_wait_for_connection, 0) ); //wait maximum 10 sec\n    if( !client.exists() ) {\n        ROS_ERROR( \"[Cerebro::descriptor_computer_thread]Connection to server NOT successful. I tried waiting for %d sec, still couldnt establish connection. Quiting the thread.\", n_sec_wait_for_connection );\n        return;\n    }\n    else std::cout << TermColor::GREEN() <<  \"Connection to ros-service ``\" << client.getService() << \"`` established\" << TermColor::RESET() << endl;\n    connected_to_descriptor_server = true;\n\n\n    ElapsedTime _est_desc_compute_time_ms;\n    {\n        auto _abs_cam = dataManager->getAbstractCameraRef();\n        assert( _abs_cam && \"[Cerebro::descriptor_computer_thread] request from cerebro to access camera from dataManager is invalid. This means that camera was not yet set in dataManager\\n\");\n        nrows = _abs_cam->imageHeight();\n        ncols = _abs_cam->imageWidth();\n        cv::Mat zero_image;\n        if( nChannels == 3 )\n            zero_image = cv::Mat::zeros( nrows, ncols, CV_8UC3 );\n        else if( nChannels == 1 )\n            zero_image = cv::Mat::zeros( nrows, ncols, CV_8UC1 );\n        else {\n            assert( false && \"Invalid number of channels specified in Cerebro::descriptor_computer_thread() \");\n            cout << TermColor::RED() << \"[ERROR] Ax Invalid number of channels specified in Cerebro::descriptor_computer_thread() \" << endl << TermColor::RESET();\n            exit(3);\n        }\n\n        // create zero image sensor_msgs::Image\n        sensor_msgs::ImagePtr image_msg;\n        if( nChannels == 3 )\n            image_msg = cv_bridge::CvImage(std_msgs::Header(), \"bgr8\", zero_image).toImageMsg();\n        else if( nChannels == 1 )\n            image_msg = cv_bridge::CvImage(std_msgs::Header(), \"mono8\", zero_image).toImageMsg();\n        else {\n            assert( false && \"Invalid number of channels specified in Cerebro::descriptor_computer_thread() \");\n            cout << TermColor::RED() << \"[ERROR] Bx Invalid number of channels specified in Cerebro::descriptor_computer_thread() \" << endl << TermColor::RESET();\n            exit(3);\n        }\n        image_msg->header.stamp = ros::Time::now();\n        cerebro::WholeImageDescriptorCompute srv; //service message\n        srv.request.ima = *image_msg;\n        srv.request.a = 986;\n        // make request to server\n\n        _est_desc_compute_time_ms.tic();\n        if( client.call( srv ) ) {\n            __Cerebro__descriptor_computer_thread(std::cout <<  \"Received response from server\\t\";)\n            __Cerebro__descriptor_computer_thread(std::cout << \"desc.size=\" << srv.response.desc.size() << std::endl;)\n            assert( srv.response.desc.size() > 0 && \"The received descriptor appear to be of zero length. This is a fatal error.\\n\" );\n            desc_size = int( srv.response.desc.size() );\n        }\n    }\n    assert( nrows > 0 && ncols > 0 && desc_size > 0 );\n    int estimated_descriptor_compute_time_ms = _est_desc_compute_time_ms.toc_milli();\n    cout << \"[Cerebro::descriptor_computer_thread] nrows=\" << nrows << \"  ncols=\" << ncols << \" nChannels=\" << nChannels << \" ===>  desc_size=\" << desc_size << \"\\t estimated_descriptor_compute_time_ms=\" << estimated_descriptor_compute_time_ms << endl;\n    this->descriptor_size = int(desc_size);\n    descriptor_size_available = true;\n\n\n\n    ros::Rate rate(20);\n    auto data_map = dataManager->getDataMapRef();\n    auto img_data_mgr = dataManager->getImageManagerRef();\n\n    #if 1\n    //*****************\n    // If loadStateFromDisk, ie. if data_map already has a lot of items means that the\n    // state was preloaded. in this case just setup this thread appropriately.\n    //**************************\n    if( data_map->begin() == data_map->end() )\n    {\n        // This means the data_map is empty, which inturn means fresh run\n        cout << TermColor::iBLUE() << \"[Cerebro::descriptor_computer_thread] Looks like data_map is empty which means this is a fresh run\" << TermColor::RESET() << endl;;\n    }\n    else {\n        // This means, state was preloaded from file\n        cout << TermColor::iGREEN() << \"[Cerebro::descriptor_computer_thread] Looks like data_map is NOT empty which means state was loaded from disk\" << TermColor::RESET() << endl;;\n\n        // loop over data_map and make a note of all the timestamps where descriptor is available.\n        int n_whlimgdescavai = 0;\n        // img_data_mgr->print_status();\n        for( auto itd=data_map->begin() ; itd!=data_map->end() ; itd++ )\n        {\n            if( itd->second->isWholeImageDescriptorAvailable() ) {\n                n_whlimgdescavai++;\n                wholeImageComputedList_pushback( itd->first );\n\n                // Also make sure these images are rechable.\n                bool kxxx = img_data_mgr->isImageRetrivable( \"left_image\", itd->first );\n                if( kxxx == false ) // this is bad\n                {\n                    cout << \"[Cerebro::descriptor_computer_thread] THIS IS BAD. REPORT IT TO AUTHORS WITH CODE jewbcjsmn\\nEXIT.....\\n\";\n                    exit(1);\n                }\n            }\n        }\n        cout << \"[Cerebro::descriptor_computer_thread]i find \"<< n_whlimgdescavai << \" datanodes where image descrptor is available and images are retrivable through the ImageDataManager\\n\";\n    }\n    #endif\n\n\n    ros::Time last_proc_timestamp =ros::Time();\n    int n_computed=0;\n    ElapsedTime _time;\n    int incoming_diff_ms = 0;\n    while( b_descriptor_computer_thread )\n    {\n        if( data_map->begin() == data_map->end() ) {\n            __Cerebro__descriptor_computer_thread( cout << \"nothing to compute descriptor\\n\" );\n            std::this_thread::sleep_for( std::chrono::milliseconds( 1000 )  );\n            continue;\n        }\n        ros::Time lb = data_map->rbegin()->first - ros::Duration(10, 0); // look at recent 10sec.\n        auto S = data_map->lower_bound( lb );\n        auto E = data_map->end();\n        __Cerebro__descriptor_computer_thread(cout << \"[Cerebro::descriptor_computer_thread]] S=\" << S->first << \"  E=\" << (data_map->rbegin())->first << endl;)\n        for( auto it = S; it != E ; it++ ) {\n\n            // cout << \"[Cerebro::descriptor_computer_thread]try : \" << it->first << \"\\t\";\n            // cout << \"isWholeImageDescriptorAvailable=\" << it->second->isWholeImageDescriptorAvailable() << \"\\t\";\n            // cout << \"isKeyFrame=\" << it->second->isKeyFrame() << \"\\t\";\n            // cout << endl;\n\n            //descriptor does not exisit at this stamp, so compute it.\n            // Here I compute the whole image descriptor only at keyframes, you may try something like, if the pose is available compute it.\n            if( it->second->isWholeImageDescriptorAvailable() == false && it->second->isKeyFrame() && it->first >  last_proc_timestamp )\n            {\n                  __Cerebro__descriptor_computer_thread(cout << TermColor::MAGENTA() << \"[Cerebro::descriptor_computer_thread]--- process t=\" << it->first << \"\\trel_t=\" <<  it->first - dataManager->getPose0Stamp() << TermColor::RESET() << \" n_computed=\" << n_computed << endl;)\n                  n_computed++;\n                  incoming_diff_ms = (it->first - last_proc_timestamp).toSec() * 1000. ;\n                  float skip_frac = 1.0 -  incoming_diff_ms/float(estimated_descriptor_compute_time_ms) ;\n                  __Cerebro__descriptor_computer_thread(  cout << \"incoming_diff_ms = \" << incoming_diff_ms << \"\\testimated_descriptor_compute_time_ms=\" << estimated_descriptor_compute_time_ms  << \" so, skip_frac=\"<< skip_frac <<  endl; )\n                  last_proc_timestamp = it->first;\n\n                  //   if( n_computed%2 == 0 ) // simple skip\n                  if( n_computed>4 && ( rand()/ float(RAND_MAX) ) < skip_frac ) // dynamic skip\n                  {\n                      __Cerebro__descriptor_computer_thread( cout << \"\\t\\t...SKIP...\\n\"; )\n                      continue;\n                  }\n\n\n                    if( it->second->getNumberOfSuccessfullyTrackedFeatures() < 20 )\n                    {\n                        __Cerebro__descriptor_computer_thread( cout << \"[Cerebro::descriptor_computer_thread] skip computing whole-image-descriptor for this image because it appears to be a kidnapped image frame.\\n\" ; )\n                        continue;\n                    }\n\n                    _time.tic();\n\n                    // use it->second->getImage() to compute descriptor. call the service\n                    cv::Mat image_curr;\n                    #if 0 // set this to 1 to get image from data_map (deprecated way), 0 to get image from image_manager.\n                    // old code in which images were stored in nodes\n                    image_curr = it->second->getImage();\n                    #else\n                    // using image data manager\n                    { //dont remove these braces, it is used for scoping and automatic deallocation\n                        cv::Mat tmp_img;\n                        bool getimstatus = img_data_mgr->getImage( \"left_image\", it->first, tmp_img );\n                        if( getimstatus == false ) {\n                            __Cerebro__descriptor_computer_thread( cout << \"since I cannot getImage, goto hurr_here\\n\"; );\n                            goto huur_here;\n                        }\n\n                        if( nChannels == 3 && tmp_img.channels() == 1 )\n                            cv::cvtColor( tmp_img, image_curr, CV_GRAY2BGR );\n                        else if( nChannels==1 && tmp_img.channels() == 3)\n                            cv::cvtColor( tmp_img, image_curr, CV_BGR2GRAY );\n                        else\n                            image_curr = tmp_img;\n                    }\n\n                    __Cerebro__descriptor_computer_thread(\n                    cout << \"image from mgr info: \" << MiscUtils::cvmat_info(image_curr) << endl;\n                    cout << \"nChannels=\" << nChannels << endl;\n                    )\n                    #endif\n\n                    sensor_msgs::ImagePtr image_msg;\n                    if( nChannels == 3 ) {\n                        image_msg = cv_bridge::CvImage(std_msgs::Header(), \"bgr8\", image_curr).toImageMsg();\n                    }\n                    else if( nChannels == 1 ) {\n                        // cout << \"cv_bridge mono8 encoding\\n\";\n                        image_msg = cv_bridge::CvImage(std_msgs::Header(), \"mono8\", image_curr).toImageMsg();\n                    }\n                    else {\n                        assert( false && \"Invalid number of channels specified in Cerebro::descriptor_computer_thread() \");\n                        cout << TermColor::RED() << \"[ERROR] Cx Invalid number of channels specified in Cerebro::descriptor_computer_thread() \" << endl << TermColor::RESET();\n                        exit(3);\n                    }\n                    image_msg->header.stamp = ros::Time( it->first );\n\n\n\n                    cerebro::WholeImageDescriptorCompute srv; //service message\n                    srv.request.ima = *image_msg;\n                    srv.request.a = 986;\n                    if( client.call( srv ) ) {\n                        // __Cerebro__descriptor_computer_thread(std::cout <<  \"Received response from server\\t\";)\n                        // __Cerebro__descriptor_computer_thread(std::cout << \"desc.size=\" << srv.response.desc.size() << std::endl;)\n                        assert( srv.response.desc.size() > 0 && \"The received descriptor appear to be of zero length. This is a fatal error.\\n\" );\n\n                        VectorXd vec( srv.response.desc.size() ); // allocated a vector\n                        for( int j=0 ; j<srv.response.desc.size() ; j++ ) {\n                            vec(j) = srv.response.desc[j];\n                        }\n                        // std::this_thread::sleep_for(std::chrono::milliseconds(100));\n\n                        it->second->setWholeImageDescriptor( vec );\n                        wholeImageComputedList_pushback( it->first );\n\n                        // __Cerebro__descriptor_computer_thread(cout << \"Computed descriptor at t=\" << it->first - dataManager->getPose0Stamp() << \"\\t\" << it->first << endl;)\n                        // __Cerebro__descriptor_computer_thread(std::cout << \"Computed descriptor in (millisec) = \" << _time.toc_milli()  << endl;)\n                        // __Cerebro__descriptor_computer_thread__imp(cout << TermColor::CYAN() << \"Computed descriptor at t=\" << it->first - dataManager->getPose0Stamp() << \"\\t\" << it->first << TermColor::RESET() << endl);\n\n                         estimated_descriptor_compute_time_ms = _time.toc_milli();\n                        __Cerebro__descriptor_computer_thread__imp(\n                        cout << \"Computed descriptor at t=\" << it->first << \" of dim=\" << srv.response.desc.size()  << \" in millisec=\" << estimated_descriptor_compute_time_ms << endl;\n                        )\n\n\n                    }\n                    else {\n                        ROS_ERROR( \"Failed to call ros service\" );\n                    }\n\n            }\n            huur_here:\n            int ddddd=0;  //this is here for the goto statement.\n        }\n\n        rate.sleep();\n    }\n\n\n    cout << \"[Cerebro::descriptor_computer_thread] Finished\\n\";\n\n}\n\n\n\n// these both functions are newly added. There are still some raw access\n// limited only to the function ``descrip_N__dot__descrip_0_N()``.\nconst int Cerebro::wholeImageComputedList_size() const {\n    std::lock_guard<std::mutex> lk(m_wholeImageComputedList);\n    return wholeImageComputedList.size();\n}\n\nconst ros::Time Cerebro::wholeImageComputedList_at(int k) const\n{\n    std::lock_guard<std::mutex> lk(m_wholeImageComputedList);\n    assert( k>=0 && k<wholeImageComputedList.size() && \"[Cerebro::wholeImageComputedList_at]\");\n    return wholeImageComputedList.at( k );\n}\n\nvoid Cerebro::wholeImageComputedList_pushback( const ros::Time __tx )\n{\n    std::lock_guard<std::mutex> lk(m_wholeImageComputedList);\n    this->wholeImageComputedList.push_back( __tx ); // note down where it was computed.\n\n}\n\n//------------------------------------------------------------------//\n// END per Keyframe Descriptor Computation\n//------------------------------------------------------------------//\n\n\n\n\n//------------------------------------------------------------------//\n//---------------- Populate Loop Candidates --------------------//\n//------------------------------------------------------------------//\n\n\n\n// #define __Cerebro__run__( msg ) msg ;\n#define __Cerebro__run__( msg ) ;\n\n// #define __Cerebro__run__debug( msg ) msg ;\n#define __Cerebro__run__debug( msg ) ;\n\n/// TODO: In the future more intelligent schemes can be experimented with. Besure to run those in new threads and disable this thread.\n/// wholeImageComputedList is a list for which descriptors are computed. Similarly other threads can compute\n/// scene-object labels, text etc etc in addition to currently computed whole-image-descriptor\nvoid Cerebro::run()\n{\n    descrip_N__dot__descrip_0_N();\n    // faiss__naive_loopcandidate_generator();\n    // faiss_clique_loopcandidate_generator();\n    // faiss_multihypothesis_tracking();\n\n}\n\n\n#ifdef HAVE_FAISS\n///-----------------------------------------------------------------\n/// Nearest neighbors search using facebook research's faiss library's IndexFlatIP\n///         Roughly follows : https://github.com/mpkuse/vins_mono_debug_pkg/blob/master/src_place_recog/faiss_try1.py\n/// This function is similar in functionality to `descrip_N__dot__descrip_0_N`\n///        but using faiss\nvoid Cerebro::faiss__naive_loopcandidate_generator()\n{\n    assert( m_dataManager_available && \"You need to set the DataManager in class Cerebro before execution of the run() thread can begin. You can set the dataManager by call to Cerebro::setDataManager()\\n\");\n    assert( b_run_thread && \"you need to call run_thread_enable() before run() can start executing\\n\" );\n\n    //-----------------//\n    //---- Settings ---//\n    //-----------------//\n    const int start_adding_descriptors_to_index_after = 150;\n    const int LOCALITY_THRESH = 12;\n    const float DOT_PROD_THRESH = 0.9;\n\n    //------ END -----//\n\n    // wait until connected_to_descriptor_server=true and descriptor_size_available=true\n    if( wait_until__connectedToDescServer_and_descSizeAvailable( 71 ) == false ) {\n        cout << TermColor::RED() << \"[Cerebro::faiss__naive_loopcandidate_generator ERROR] wait_until__connectedToDescServer_and_descSizeAvailable returned false implying a timeout.\\n\" << TermColor::RESET();\n        return;\n    }\n    __Cerebro__run__( cout << TermColor::GREEN() <<\"[Cerebro::faiss__naive_loopcandidate_generator] descriptor_size=\" << this->descriptor_size << \"  connected_to_descriptor_server && descriptor_size_available\" << TermColor::RESET() << endl; )\n    assert( this->descriptor_size> 0 );\n\n\n    cout << TermColor::GREEN() << \"init a faiss::IndexFlatIP(\"<< this->descriptor_size << \")\" << TermColor::RESET() << endl;\n    faiss::IndexFlatIP index(this->descriptor_size);\n\n\n    ros::Rate rate(10);\n    int l=0, last_l=0;\n    int l_last_added_to_index = 0;\n\n    auto data_map = dataManager->getDataMapRef();\n    while( b_run_thread )\n    {\n        l=wholeImageComputedList_size();\n\n        if( l - last_l < 3 ) {\n            __Cerebro__run__debug( cout << \"[Cerebro::faiss__naive_loopcandidate_generator]nothing new\\n\"; );\n            rate.sleep();\n            continue;\n        }\n\n        __Cerebro__run__( cout << TermColor::RED() << \"---\" << TermColor::RESET() << endl; )\n        __Cerebro__run__( cout << \"l=\" << l << endl; )\n        __Cerebro__run__debug( cout << \"[Cerebro::faiss__naive_loopcandidate_generator] data_map.size() = \" << data_map->size() << \"\\tper_image_descriptor_size=\" << this->descriptor_size << endl; )\n\n\n        // add\n        __Cerebro__run__(cout << TermColor::YELLOW();)\n        if( l> start_adding_descriptors_to_index_after ) {\n            for( int j=l_last_added_to_index ; j<l-start_adding_descriptors_to_index_after ; j++ ) {\n                __Cerebro__run__(\n                cout << \"index.add( \" << j << \")\" ;\n                cout << \"\\t aka  index.add(\" << wholeImageComputedList_at(j) << \")\";\n                cout << endl;)\n                VectorXd X = data_map->at( wholeImageComputedList_at( j ) )->getWholeImageDescriptor();\n                VectorXf X_float = X.cast<float>();\n                float * X_raw = X_float.data();\n                #if 0 //see if the type casting was correct.\n                for( int g=0;g<5;g++ ) {\n                    // cout << \"(\" << X(g) << \",\" << X_float(g) << \") \";\n                    cout << \"(\" << X(g) << \",\" << X_raw[g] << \") \";\n                }\n                cout << endl;\n                #endif\n                index.add( 1, X_raw ); //-TODO\n            }\n            l_last_added_to_index = l-start_adding_descriptors_to_index_after;\n        } else {\n            __Cerebro__run__(cout << \"not seen enough. so,, add nothing. Will start adding after \"<< start_adding_descriptors_to_index_after<< \" descriptors\\n\";)\n        }\n        __Cerebro__run__(cout << TermColor::RESET();)\n\n        // search\n        vector<float> tmp_;\n        vector<int> tmp_i;\n        for( int l_i=last_l ; l_i<l; l_i++ ) {\n            __Cerebro__run__(\n            cout << TermColor::MAGENTA();\n            cout << \"index.search(\" << l_i << \")\\t\";\n            cout << \"index.search(\" << wholeImageComputedList_at(l_i) << \")\\t\";\n            cout << \"index.ntotal=\" << index.ntotal << \"\\t\";\n            // cout << endl;\n            )\n\n            if( index.ntotal < 5 )\n                continue;\n\n            VectorXd X = data_map->at( wholeImageComputedList_at( l_i ) )->getWholeImageDescriptor();\n            VectorXf X_float = X.cast<float>();\n            float * X_raw = X_float.data();\n            float distances[5];\n            faiss::Index::idx_t labels[5];\n            ElapsedTime time_to_search = ElapsedTime();\n            index.search( 1, X_raw, 5, distances, labels ); //TODO\n            __Cerebro__run__(cout << \"search done in (ms): \" << time_to_search.toc_milli() << endl;)\n            for( int g=0 ; g<5; g++ ) {\n                __Cerebro__run__(\n                // cout << g << \" labels=\" << labels[g] << \" distances=\" << distances[g] << endl;\n                // cout << l_i << \"<--(\"<< distances[g] << \")-->\" << labels[g] << \"\\t\";\n                printf( \"%4d<--(%4.2f)-->%d\\t\", l_i, distances[g], labels[g] );\n                )\n            }\n            __Cerebro__run__(cout << endl;)\n            __Cerebro__run__(cout << TermColor::RESET();)\n            tmp_.push_back(  distances[0] );\n            tmp_i.push_back( labels[0] );\n        }\n\n        int _n = tmp_.size();\n        if( _n ==3 && tmp_[_n-1] > DOT_PROD_THRESH && abs(tmp_i[0] - tmp_i[1]) < LOCALITY_THRESH && abs(tmp_i[0] - tmp_i[2]) < LOCALITY_THRESH  )\n        {\n            __Cerebro__run__(\n            cout << TermColor::RED() << \"loop found\" << l-1 << \"<--->\" << tmp_i[2] << TermColor::RESET();\n            )\n\n            {\n            std::lock_guard<std::mutex> lk_foundloops(m_foundLoops);\n            foundLoops.push_back( std::make_tuple( wholeImageComputedList_at(l-1), wholeImageComputedList_at(tmp_i[2]), tmp_[2] ) );\n            }\n        }\n\n        last_l = l;\n        rate.sleep();\n    }\n    cout << \"[Cerebro::faiss__naive_loopcandidate_generator()] Finished\\n\";\n}\n\n\n// #define __faiss_clique_loopcandidate_generator__imp(msg) msg;\n#define __faiss_clique_loopcandidate_generator__imp(msg) ;\n\n// #define __faiss_clique_loopcandidate_generator__debug(msg) msg;\n#define __faiss_clique_loopcandidate_generator__debug(msg) ;\n\n// #define __faiss_clique_loopcandidate_generator__addtoindex(msg) msg;\n#define __faiss_clique_loopcandidate_generator__addtoindex(msg) ;\n\n#define __faiss_clique_loopcandidate_generator__search(msg) msg;\n// #define __faiss_clique_loopcandidate_generator__search(msg) ;\nvoid Cerebro::faiss_clique_loopcandidate_generator()\n{\n    assert( m_dataManager_available && \"You need to set the DataManager in class Cerebro before execution of the run() thread can begin. You can set the dataManager by call to Cerebro::setDataManager()\\n\");\n    assert( b_run_thread && \"you need to call run_thread_enable() before run() can start executing\\n\" );\n    //-----------------//\n    //---- Settings ---//\n    //-----------------//\n    const int start_adding_descriptors_to_index_after = 150;\n    const int K_NEAREST_NEIGHBOURS=5;\n    const double DOT_PROD_THRESH = 0.85; //0.85\n    const int LOCALITY = 7;//7\n    const int reset_accumulation_every_n_frames = 4; //4\n\n    // wait until connected_to_descriptor_server=true and descriptor_size_available=true\n    if( wait_until__connectedToDescServer_and_descSizeAvailable( 71 ) == false ) {\n        cout << TermColor::RED() << \"[Cerebro::faiss__naive_loopcandidate_generator ERROR] wait_until__connectedToDescServer_and_descSizeAvailable returned false implying a timeout.\\n\" << TermColor::RESET();\n        return;\n    }\n    __faiss_clique_loopcandidate_generator__imp( cout << TermColor::GREEN() <<\"[Cerebro::faiss__naive_loopcandidate_generator] descriptor_size=\" << this->descriptor_size << \"  connected_to_descriptor_server && descriptor_size_available\" << TermColor::RESET() << endl; )\n    assert( this->descriptor_size> 0 );\n\n    // init faiss index\n    __faiss_clique_loopcandidate_generator__imp(\n    cout << TermColor::GREEN() << \"init a faiss::IndexFlatIP(\"<< this->descriptor_size << \")\" << TermColor::RESET() << endl;\n    )\n    faiss::IndexFlatIP index(this->descriptor_size);\n\n    ros::Rate rate(10);\n    auto data_map = dataManager->getDataMapRef();\n\n    int l=0, last_l=0, l_last_added_to_index=0;\n    std::map< int, std::map< int, float> > data_strc;\n\n\n    float * distances = new float[K_NEAREST_NEIGHBOURS];\n    faiss::Index::idx_t * labels = new faiss::Index::idx_t[K_NEAREST_NEIGHBOURS];\n    map<faiss::Index::idx_t, int> retained; //< Accumulation map. reset every say 3 indices. tune this as per amount of computation available\n\n    while( b_run_thread ) {\n        l = wholeImageComputedList_size();\n        if( l<= last_l ) {\n            __faiss_clique_loopcandidate_generator__debug( cout << \"[Cerebro::faiss_clique_loopcandidate_generator] nothing new\\n\";)\n            rate.sleep();\n            continue;\n        }\n\n        __faiss_clique_loopcandidate_generator__imp(\n        cout << TermColor::RED() << \"--- \" << TermColor::RESET() << l << \"\\t\";\n        cout << \"new [\" << last_l << \" to \" << l << \")\\n\";\n        )\n\n\n        //--------- add\n        __faiss_clique_loopcandidate_generator__addtoindex(cout << TermColor::YELLOW();)\n        if( l>start_adding_descriptors_to_index_after) {\n            for( int j=l_last_added_to_index ; j<l-start_adding_descriptors_to_index_after ; j++ )\n            {\n                __faiss_clique_loopcandidate_generator__addtoindex(\n                cout << \"index.add( \" << j << \")\" ;\n                cout << \"\\t aka  index.add(\" << wholeImageComputedList_at(j) << \")\\n\";\n                )\n\n                VectorXd X = data_map->at( wholeImageComputedList_at( j ) )->getWholeImageDescriptor();\n                VectorXf X_float = X.cast<float>();\n                float * X_raw = X_float.data();\n                #if 0 //see if the type casting was correct.\n                for( int g=0;g<5;g++ ) {\n                    // cout << \"(\" << X(g) << \",\" << X_float(g) << \") \";\n                    cout << \"(\" << X(g) << \",\" << X_raw[g] << \") \";\n                }\n                cout << endl;\n                #endif\n                index.add( 1, X_raw );\n            }\n            l_last_added_to_index = l-start_adding_descriptors_to_index_after;\n        } else {\n            __faiss_clique_loopcandidate_generator__addtoindex(\n            cout << \"not seen enough. so,, add nothing. Will start adding after \"<< start_adding_descriptors_to_index_after<< \" descriptors\\n\";\n            )\n        }\n        __faiss_clique_loopcandidate_generator__addtoindex(cout << TermColor::RESET();)\n\n\n\n        //----------- search\n        __faiss_clique_loopcandidate_generator__search(cout << TermColor::MAGENTA();)\n        for( int l_i = last_l ; l_i < l ; l_i++ )\n        {\n            __faiss_clique_loopcandidate_generator__search(\n            cout << \"\\t\\tindex.search(\" << l_i << \")\\t\";\n            cout << \"index.search(\" << wholeImageComputedList_at(l_i) << \")\\t\";\n            cout << \"index.ntotal=\" << index.ntotal << \"\\n\";\n            )\n            if( index.ntotal < K_NEAREST_NEIGHBOURS )\n                break;\n\n            // l_i's descriptor\n            VectorXd X = data_map->at( wholeImageComputedList_at( l_i ) )->getWholeImageDescriptor();\n            VectorXf X_float = X.cast<float>();\n            float * X_raw = X_float.data();\n\n            // ElapsedTime time_to_search = ElapsedTime();\n            index.search( 1, X_raw, K_NEAREST_NEIGHBOURS, distances, labels );\n            // cout << \"search done in (ms): \" << time_to_search.toc_milli() << endl;\n\n            __faiss_clique_loopcandidate_generator__search(\n            // Printing\n            for( int g=0 ; g<K_NEAREST_NEIGHBOURS; g++ ) {\n                if( distances[g] > DOT_PROD_THRESH )\n                    cout << TermColor::GREEN();\n                else cout << TermColor::MAGENTA();\n                printf( \"%4d<--(%4.2f)-->%d\\t\", l_i, distances[g], labels[g] );\n                cout << TermColor::RESET();\n            }\n            cout << endl;\n            )\n\n\n            // Go thru each nearest neighbours and add to list separated ones.                                              vv same as 168, ignore\n            // for example, if the neighbours: `802<--(0.92)-->606\t 802<--(0.89)-->168\t 802<--(0.89)-->172\t 802<--(0.89)-->169\t 802<--(0.88)-->607`\n            //                                                 ^^                   ^^                  ^^same as 168, so ignore                ^^ same as 606, ignore\n\n            // cout << \"loop thru \" << K_NEAREST_NEIGHBOURS << \"\\n\";\n            for( int g=0 ; g < K_NEAREST_NEIGHBOURS ; g++ )\n            {\n                if( distances[g] < DOT_PROD_THRESH ) //since distances are arranged in decending order, if you start seeing below my threshold its time to not process further.\n                    break;\n\n                // cout << \"g=\"<<g << \", label=\"<< labels[g] << \", dis=\"<< distances[g] ;\n                // cout << \"\\tloop thru retained. retained.size()=\" << retained.size() << endl;\n                faiss::Index::idx_t duplicate = -1;\n                for( auto ity=retained.begin() ; ity!=retained.end() ; ity++ )\n                {\n                    // cout << \"\\tity->first=\" << ity->first << endl;\n                    if( (ity->first - labels[g]) < LOCALITY )\n                    {\n                        // cout << \"\\tcond satisfied\\n\";\n                        duplicate = ity->first;\n                        break;\n                    }\n                }\n                if( duplicate != -1 ) {\n                    // cout << \"\\tthis was not uniq so increment the key=\" << duplicate << \" by 1\\n\";\n                    retained[ duplicate ]++;\n                } else {\n                    // cout << \"\\tadd new key at \" << labels[g] << endl;\n                    retained[ labels[g] ] = 1;\n                }\n            }\n\n\n            if( retained.size() > 0 && l_i%reset_accumulation_every_n_frames == 0)\n            {\n                __faiss_clique_loopcandidate_generator__search(\n                cout << TermColor::iYELLOW() << \"l_i=\" << l_i << \" retained (ie. added to `foundLoops`) cout all: \";\n                for( auto ity=retained.begin() ; ity!=retained.end() ; ity++ )\n                {\n                    cout << ity->first << \":\" << ity->second << \", \";\n                }\n                cout << TermColor::RESET() << endl;\n                )\n\n                //%%%%\n                //%%%% NOTE: If you put all the feasible candidates into the foundLoops it causes\n                //%%%%       The pose computation to queue-up, since the number of candidates are too many.\n                //%%%%       Usually addition of more than 2 candidates at a time spells trouble.\n                //%%%%       I use simple heuristics when there are more than 3 items in the retained.\n\n\n                if( retained.size() == 1 )\n                {\n                    __faiss_clique_loopcandidate_generator__search(\n                    cout << \"only 1 item in retained, pushback \" << wholeImageComputedList_at(l-1) << \"<--->\" <<\n                                                        wholeImageComputedList_at(retained.begin()->first) << endl;\n                                                    )\n                    std::lock_guard<std::mutex> lk_foundloops(m_foundLoops);\n                    // __faiss_clique_loopcandidate_generator__search( cout << \"pushback: \" << wholeImageComputedList_at(l-1) << \"<--->\" << wholeImageComputedList_at(retained.begin()->first) << endl; )\n                    foundLoops.push_back( std::make_tuple( wholeImageComputedList_at(l-1),\n                                                        wholeImageComputedList_at(retained.begin()->first ),\n                                                        0.9 ) );\n                }\n\n                if( retained.size() > 1 )\n                {\n                    int percent = 100. / retained.size(); //will retain this many\n                    __faiss_clique_loopcandidate_generator__search( cout << \"Retain %=\" << percent << endl; )\n                    for( auto ity=retained.begin() ; ity!=retained.end() ; ity++ )\n                    {\n                        if( rand()%100 < percent ) {\n                            std::lock_guard<std::mutex> lk_foundloops(m_foundLoops);\n                            __faiss_clique_loopcandidate_generator__search( cout << \"pushback: \" << wholeImageComputedList_at(l-1) << \"<--->\" << wholeImageComputedList_at(ity->first) << endl; )\n                            foundLoops.push_back( std::make_tuple( wholeImageComputedList_at(l-1),\n                                                            wholeImageComputedList_at(ity->first ),\n                                                            0.9 ) );\n                        }\n                    }\n                }\n\n\n                retained.clear();\n            }\n\n\n        }\n\n\n        last_l = l;\n        rate.sleep();\n    }\n\n\n    delete [] distances;\n    delete [] labels;\n    cout << \"[Cerebro::faiss_clique_loopcandidate_generator] Finished Thread\\n\";\n\n\n}\n\n\n\n#define ___faiss_multihypothesis_tracking___add(msg) msg;\n// #define ___faiss_multihypothesis_tracking___add(msg) ;\n\n#define ___faiss_multihypothesis_tracking___search(msg) msg;\n// #define ___faiss_multihypothesis_tracking___search(msg) ;\nvoid Cerebro::faiss_multihypothesis_tracking()\n{\n    assert( m_dataManager_available && \"You need to set the DataManager in class Cerebro before execution of the run() thread can begin. You can set the dataManager by call to Cerebro::setDataManager()\\n\");\n    assert( b_run_thread && \"you need to call run_thread_enable() before run() can start executing\\n\" );\n    //-----------------//\n    //---- Settings ---//\n    //-----------------//\n    const int start_adding_descriptors_to_index_after = 150;\n    const int K_NEAREST_NEIGHBOURS=5;\n\n    //-----------------//\n    //----   Wait  ----//\n    //-----------------//\n    // wait until connected_to_descriptor_server=true and descriptor_size_available=true\n    if( wait_until__connectedToDescServer_and_descSizeAvailable( 71 ) == false ) {\n        cout << TermColor::RED() << \"[Cerebro::faiss__naive_loopcandidate_generator ERROR] wait_until__connectedToDescServer_and_descSizeAvailable returned false implying a timeout.\\n\" << TermColor::RESET();\n        return;\n    }\n    cout << TermColor::GREEN() <<\"[Cerebro::faiss_multihypothesis_tracking] descriptor_size=\" << this->descriptor_size << \"  connected_to_descriptor_server && descriptor_size_available\" << TermColor::RESET() << endl;\n    assert( this->descriptor_size> 0 );\n\n    //---------------------//\n    //--- init faiss index //\n    //---------------------//\n    cout << TermColor::GREEN() << \"[[Cerebro::faiss_multihypothesis_tracking]] init a faiss::IndexFlatIP(\"<< this->descriptor_size << \")\" << TermColor::RESET() << endl;\n    faiss::IndexFlatIP index(this->descriptor_size);\n\n\n    // init (misc)\n    ros::Rate rate(10);\n    auto data_map = dataManager->getDataMapRef();\n    HypothesisManager * hyp_manager = new HypothesisManager(); // there is a delete at the end of this function\n\n    // Start monitoring thread\n    hyp_manager->monitoring_thread_enable();\n    // hyp_manager->monitoring_thread_disable();\n    std::thread hyp_monitoring_th( &HypothesisManager::monitoring_thread, hyp_manager );\n\n\n\n    int l=0, last_l=0, l_last_added_to_index=0;\n\n\n    float * distances = new float[K_NEAREST_NEIGHBOURS];\n    faiss::Index::idx_t * labels = new faiss::Index::idx_t[K_NEAREST_NEIGHBOURS];\n\n    //---------------------//\n    // While\n    //      index.add( i-150 )\n    //      index.search( i )\n    //---------------------//\n    while( b_run_thread ) {\n        l = wholeImageComputedList_size();\n        if( l<= last_l ) {\n            cout << \"[Cerebro::faiss_multihypothesis_tracking] nothing new\\n\";\n            rate.sleep();\n            continue;\n        }\n\n        // looks like new descriptors are available.\n        cout << TermColor::RED() << \"--- \" << TermColor::RESET() << l << \"\\t\";\n        cout << \"new [\" << last_l << \" to \" << l << \")\\n\";\n\n\n\n        //--------- add\n        ___faiss_multihypothesis_tracking___add(cout << TermColor::YELLOW();)\n        if( l>start_adding_descriptors_to_index_after) {\n            for( int j=l_last_added_to_index ; j<l-start_adding_descriptors_to_index_after ; j++ )\n            {\n                ___faiss_multihypothesis_tracking___add(\n                cout << \"index.add( \" << j << \")\" ;\n                cout << \"\\t aka  index.add(\" << wholeImageComputedList_at(j) << \")\\n\";)\n\n\n                VectorXd X = data_map->at( wholeImageComputedList_at( j ) )->getWholeImageDescriptor();\n                VectorXf X_float = X.cast<float>();\n                float * X_raw = X_float.data();\n                #if 0 //see if the type casting was correct.\n                for( int g=0;g<5;g++ ) {\n                    // cout << \"(\" << X(g) << \",\" << X_float(g) << \") \";\n                    cout << \"(\" << X(g) << \",\" << X_raw[g] << \") \";\n                }\n                cout << endl;\n                #endif\n                index.add( 1, X_raw );\n            }\n            l_last_added_to_index = l-start_adding_descriptors_to_index_after;\n        } else {\n            ___faiss_multihypothesis_tracking___add(\n                cout << \"not seen enough. so,, add nothing to the index. Will start adding after \"<< start_adding_descriptors_to_index_after<< \" descriptors\\n\";\n            )\n\n        }\n        ___faiss_multihypothesis_tracking___add(cout << TermColor::RESET();)\n\n\n\n        //----------- search\n        cout << TermColor::MAGENTA();\n        for( int l_i = last_l ; l_i < l ; l_i++ ) {\n            cout << \"\\t\\tindex.search(\" << l_i << \")\\t\";\n            cout << \"index.search(\" << wholeImageComputedList_at(l_i) << \")\\t\";\n            cout << \"index.ntotal=\" << index.ntotal << \"\\n\";\n            if( index.ntotal < K_NEAREST_NEIGHBOURS )\n                break;\n\n            // l_i's descriptor\n            VectorXd X = data_map->at( wholeImageComputedList_at( l_i ) )->getWholeImageDescriptor();\n            VectorXf X_float = X.cast<float>();\n            float * X_raw = X_float.data();\n\n            // ElapsedTime time_to_search = ElapsedTime();\n            index.search( 1, X_raw, K_NEAREST_NEIGHBOURS, distances, labels );\n            // cout << \"search done in (ms): \" << time_to_search.toc_milli() << endl;\n\n            // Loop over all the nearest neighbours\n            cout << \"\\t\\t\";\n            for( int g=0 ; g<K_NEAREST_NEIGHBOURS; g++ ) {\n                if( distances[g] > 0.85 )\n                    cout << TermColor::GREEN();\n                else cout << TermColor::MAGENTA();\n                printf( \"g=%d: %4d<--(%4.2f)-->%d\\t\", g, l_i, distances[g], labels[g] );\n                cout << TermColor::RESET();\n\n\n\n                if( distances[g] > 0.85  ) {\n                    // Send (l_i, labels[g], distances[g] ) to HypothesisManager\n                    hyp_manager->add_node( l_i, labels[g], distances[g] );\n                }\n            }\n            cout << endl ;\n\n            // Loop through each hypothesis and decrement the time-to-live\n            hyp_manager->digest();\n        }\n\n\n        last_l = l;\n        rate.sleep();\n    }\n\n\n    hyp_manager->monitoring_thread_disable();\n    hyp_monitoring_th.join();\n\n\n    delete [] distances;\n    delete [] labels;\n    delete hyp_manager;\n    cout << \"[Cerebro::faiss_multihypothesis_tracking] Finished Thread\\n\";\n\n\n}\n\n\n\n\n#endif //HAVE_FAISS\n\n///-----------------------------------------------------------------\n\n///-----------------------------------------------------------------\n/// This implements a simple loopclosure detection scheme based on dot-product of descriptor-vectors.\n///\n\n// 1 will plot the result of dot product as image. 0 will not plot to image\n// #define __Cerebro__descrip_N__dot__descrip_0_N__implotting 0\n#define __Cerebro__descrip_N__dot__descrip_0_N__implotting 1\n\n\nvoid Cerebro::descrip_N__dot__descrip_0_N()\n{\n    assert( m_dataManager_available && \"You need to set the DataManager in class Cerebro before execution of the run() thread can begin. You can set the dataManager by call to Cerebro::setDataManager()\\n\");\n    assert( b_run_thread && \"you need to call run_thread_enable() before run() can start executing\\n\" );\n\n    cout << TermColor::GREEN() << \"[Cerebro::descrip_N__dot__descrip_0_N] Start thread\" << TermColor::RESET() << endl;\n    //---\n    //--- Main settings for this function\n    //---\n    int LOCALITY_THRESH = 12;\n    float DOT_PROD_THRESH = 0.85;\n    const int start_adding_descriptors_to_index_after = 50;\n\n    ros::Rate rate(10);\n\n    #if 0\n    // wait until connected_to_descriptor_server=true and descriptor_size_available=true\n    int wait_itr = 0;\n    while( true ) {\n        if( this->connected_to_descriptor_server && this->descriptor_size_available)\n            break;\n        __Cerebro__run__(cout << wait_itr << \" [Cerebro::descrip_N__dot__descrip_0_N]waiting for `descriptor_size_available` to be true\\n\";)\n        rate.sleep();\n        wait_itr++;\n        if( wait_itr > 157 ) {\n            __Cerebro__run__( cout << TermColor::RED() << \"[Cerebro::descrip_N__dot__descrip_0_N] `this->connected_to_descriptor_server && this->descriptor_size_available` has not become true dispite waiting for about 15sec. So quiting the run thread.\\n\" << TermColor::RESET(); )\n            return;\n        }\n    }\n    #endif\n\n    if( wait_until__connectedToDescServer_and_descSizeAvailable( 71 ) == false ) {\n        cout << TermColor::RED() << \"[ Cerebro::descrip_N__dot__descrip_0_N ERROR] wait_until__connectedToDescServer_and_descSizeAvailable returned false implying a timeout.\\n\" << TermColor::RESET();\n        return;\n    }\n\n    __Cerebro__run__( cout << TermColor::GREEN() <<\"[Cerebro::descrip_N__dot__descrip_0_N] descriptor_size=\" << this->descriptor_size << \"  connected_to_descriptor_server && descriptor_size_available\" << TermColor::RESET() << endl; )\n    assert( this->descriptor_size> 0 );\n\n\n\n    int l=0, last_l=0;\n    int last_processed=0;\n    MatrixXd M = MatrixXd::Zero( this->descriptor_size, 29000 ); // TODO: Need dynamic allocation here.\n    cout << \"[Cerebro::descrip_N__dot__descrip_0_N] M.rows = \" << M.rows() << \"  M.cols=\" << M.cols()  << endl;\n    cout << \"[Cerebro::descrip_N__dot__descrip_0_N] TODO: Need dynamic allocation here.\\n\";\n\n    #if __Cerebro__descrip_N__dot__descrip_0_N__implotting > 0\n    // Plotting image\n    Plot2Mat handle(320,240, cv::Scalar(80,80,80) );\n    // Plot2Mat handle;\n    handle.setYminmax( -0.1, 1.1);\n    #endif\n    while( b_run_thread )\n    {\n\n        auto data_map = dataManager->getDataMapRef(); // this needs to be get every iteration else i dont get the ubdated values which are constantly being updated by other threads.\n        l = wholeImageComputedList_size();\n\n        if( l - last_l < 3 ) {\n            __Cerebro__run__debug( cout << \"[Cerebro::descrip_N__dot__descrip_0_N]nothing new\\n\"; );\n            rate.sleep();\n            continue;\n        }\n\n        __Cerebro__run__( cout << TermColor::RED() << \"---\" << TermColor::RESET() << endl; )\n        __Cerebro__run__( cout << \"l=\" << l << endl; )\n        __Cerebro__run__debug( cout << \"[Cerebro::descrip_N__dot__descrip_0_N] data_map.size() = \" << data_map->size() << \"\\tper_image_descriptor_size=\"<< this->descriptor_size << endl; )\n\n\n        ///------------ Extract v, v-1, v-2. The latest 3 descriptors.\n        VectorXd v, vm, vmm;\n        ros::Time i_v, i_vm, i_vmm;\n        {\n            // std::lock_guard<std::mutex> lk(m_wholeImageComputedList);\n            assert(  data_map.count( wholeImageComputedList_at(l-1) ) > 0  &&\n                     data_map.count( wholeImageComputedList_at(l-2) ) > 0  &&\n                     data_map.count( wholeImageComputedList_at(l-3) ) > 0 &&\n                     \"either of l, l-1, l-2 is not available in the datamap\"\n                 );\n\n\n\n\n            v   = data_map->at( wholeImageComputedList_at(l-1) )->getWholeImageDescriptor();\n            vm  = data_map->at( wholeImageComputedList_at(l-2) )->getWholeImageDescriptor();\n            vmm = data_map->at( wholeImageComputedList_at(l-3) )->getWholeImageDescriptor();\n            i_v = wholeImageComputedList_at(l-1);\n            i_vm = wholeImageComputedList_at(l-2);\n            i_vmm = wholeImageComputedList_at(l-3);\n        }\n\n\n\n        // This is very inefficient. Better to have a matrix-vector product and not getWholeImageDescriptor() all the time.\n        assert( M.rows() == v.size() );\n        assert( l < M.cols() );\n\n\n        ////-------------- Fill descriptors [last_l, l) into M.\n        {\n            // std::lock_guard<std::mutex> lk(m_wholeImageComputedList); //no need for this as we switched to _at functions which are threadsafe\n            for( int _s=last_l ; _s<l ; _s++ ) {\n                M.col(_s) = data_map->at( wholeImageComputedList_at(_s) )->getWholeImageDescriptor();\n\n                __Cerebro__run__debug(\n                cout << \"M.col(\" << _s << \") = data_map[ \" << wholeImageComputedList_at(_s) << \" ]. \\t\";\n                cout << \" isWholeImageDescriptorAvailable = \" << data_map->at( wholeImageComputedList_at(_s) )->isWholeImageDescriptorAvailable() << endl;\n                )\n            }\n        }\n\n\n        //////////////////////////////////////\n        ////----------- DOT PRODUCT----------\n        /////////////////////////////////////\n        int k = l - start_adding_descriptors_to_index_after; // given a stamp, l, get another stamp k. better make this to 200.\n\n        //usable size of M is 8192xl, let k (k<l) be the length until which dot is needed by time.\n        if( k > 5 ) {\n            ElapsedTime timer;\n            timer.tic();\n            // dot\n            VectorXd u   = v.transpose() * M.leftCols( k );\n            VectorXd um  = vm.transpose() * M.leftCols( k );\n            VectorXd umm = vmm.transpose() * M.leftCols( k );\n            __Cerebro__run__( cout << \"<v=\" << i_v  <<\" , M[0 to \"<< k << \"]>  \";)\n            __Cerebro__run__( cout << \"<vm=\" << i_vm  <<\" , M[0 to \"<< k << \"]>  \";)\n            __Cerebro__run__( cout << \"<vmm=\" << i_vmm  <<\" , M[0 to \"<< k << \"]>     \";)\n            __Cerebro__run__( cout << \"Done in (ms): \" << timer.toc_milli() << endl; )\n\n            // max coefficient and the index of maxcoeff.\n            double u_max = u.maxCoeff();\n            double um_max = um.maxCoeff();\n            double umm_max = umm.maxCoeff();\n            int u_argmax=-1, um_argmax=-1, umm_argmax=-1;\n            for( int ii=0 ; ii<u.size() ; ii++ ) {\n                if( u(ii) == u_max ) u_argmax = ii;\n                if( um(ii) == um_max ) um_argmax = ii;\n                if( umm(ii) == umm_max ) umm_argmax = ii;\n            }\n            assert( u_argmax > 0 && um_argmax > 0 && umm_argmax > 0 );\n\n\n            #if __Cerebro__descrip_N__dot__descrip_0_N__implotting > 0\n            // plot\n            handle.resetCanvas();\n            handle.plot( u );\n\n            #endif\n\n\n            // Criteria for a recognized place\n            if( abs(u_argmax - um_argmax) < LOCALITY_THRESH && abs(u_argmax-umm_argmax) < LOCALITY_THRESH && u_max > DOT_PROD_THRESH  )\n            {\n                // std::lock_guard<std::mutex> lk(m_wholeImageComputedList); //no need of lock here as we switched to _at functions for whlist\n\n\n                __Cerebro__run__(\n                cout << TermColor::RED() << \"Loop FOUND!! a_idx=\" << l-1 << \"<-----> (\" <<   u_argmax << \",\" << um_argmax << \",\" << umm_argmax << \")\"<< TermColor::RESET() << endl;\n                cout << TermColor::RED() << \"loop FOUND!! \"\n                                         <<  \"t_l=\" << wholeImageComputedList_at(l-1)\n                                         << \"  <DOT=\" << u_max << \">  \"\n                                         << \"t_argmax=\" << wholeImageComputedList_at(u_argmax)\n                                         << TermColor::RESET() << endl;\n                                )\n\n                  #if __Cerebro__descrip_N__dot__descrip_0_N__implotting > 0\n                  handle.mark( u_argmax );\n                  #endif\n\n                  //TODO :\n                  // publish the image pair based on a config_file_flag\n                  // read flags for publish image, threshold(0.92), locality threshold (8) from file.\n\n                {\n                std::lock_guard<std::mutex> lk_foundloops(m_foundLoops);\n                foundLoops.push_back( std::make_tuple( wholeImageComputedList_at(l-1), wholeImageComputedList_at(u_argmax), u_max ) );\n                }\n            }\n\n\n            #if __Cerebro__descrip_N__dot__descrip_0_N__implotting > 0\n            cv::imshow(\"canvas\", handle.getCanvasConstPtr() );\n            cv::waitKey(20);\n            #endif\n\n        }\n        else {\n            __Cerebro__run__(\n            cout << \"[Cerebro::descrip_N__dot__descrip_0_N] do nothing. not seen enough yet.\\n\";\n            )\n        }\n\n\n        last_l = l;\n        rate.sleep();\n    }\n\n    cout << TermColor::RED() << \"[Cerebro::descrip_N__dot__descrip_0_N] Finished thread\" << TermColor::RESET() << endl;\n}\n\n\n\n//------------------------------------------------------------------//\n//---------------- END Populate Loop Candidates --------------------//\n//------------------------------------------------------------------//\n\n\n\nconst int Cerebro::foundLoops_count() const\n{\n    std::lock_guard<std::mutex> lk(m_foundLoops);\n    return foundLoops.size();\n}\n\nconst std::tuple<ros::Time, ros::Time, double> Cerebro::foundLoops_i( int i) const\n{\n    std::lock_guard<std::mutex> lk(m_foundLoops);\n    assert( i >= 0 && i<foundLoops.size() && \"[Cerebro::foundLoops_i] you requested a loop on in range\\n\");\n    return foundLoops[i];\n}\n\n\njson Cerebro::foundLoops_as_JSON()\n{\n    std::lock_guard<std::mutex> lk(m_foundLoops);\n\n    json jsonout_obj;\n    auto data_map = dataManager->getDataMapRef();\n\n    int n_foundloops = foundLoops.size();\n    for( int i=0 ; i<n_foundloops ; i++ ) {\n        auto u = foundLoops[ i ];\n        ros::Time t_curr = std::get<0>(u);\n        ros::Time t_prev = std::get<1>(u);\n        double score = std::get<2>(u);\n\n\n        assert( data_map.count( t_curr ) > 0 && data_map.count( t_prev ) > 0  && \"One or both of the timestamps in foundloops where not in the data_map. This cannot be happening...fatal...\\n\" );\n        int idx_1 = std::distance( data_map->begin(), data_map->find( t_curr )  );\n        int idx_2 = std::distance( data_map->begin(), data_map->find( t_prev )  );\n        // cout << \"loop#\" << i << \" of \" << n_foundloops << \": \";\n        // cout << t_curr << \"(\" << score << \")\" << t_prev << endl;\n        // cout << idx_1 << \"<--->\" << idx_2 << endl;\n\n        json _cur_json_obj;\n        _cur_json_obj[\"time_sec_a\"] = t_curr.sec;\n        _cur_json_obj[\"time_nsec_a\"] = t_curr.nsec;\n        _cur_json_obj[\"time_sec_b\"] = t_prev.sec;\n        _cur_json_obj[\"time_nsec_b\"] = t_prev.nsec;\n        _cur_json_obj[\"time_double_a\"] = t_curr.toSec();\n        _cur_json_obj[\"time_double_b\"] = t_prev.toSec();\n        _cur_json_obj[\"global_a\"] = idx_1;\n        _cur_json_obj[\"global_b\"] = idx_2;\n        _cur_json_obj[\"score\"] = score;\n        jsonout_obj.push_back( _cur_json_obj );\n    }\n\n    return jsonout_obj;\n\n}\n\n//--------------------------------------------------------------//\n//------------------ Geometry Thread ---------------------------//\n//--------------------------------------------------------------//\n\n// #define __Cerebro__loopcandi_consumer__(msg) msg;\n#define __Cerebro__loopcandi_consumer__(msg)  ;\n// ^This will also imshow image-pairs with gms-matches marked.\n\n// #define __Cerebro__loopcandi_consumer__IMP( msg ) msg;\n#define __Cerebro__loopcandi_consumer__IMP( msg ) ;\n// ^Important Text only printing\n\n\n#define __Cerebro__loopcandi_consumer__IMSHOW 0 // will not produce the images (ofcourse will not show as well)\n// #define __Cerebro__loopcandi_consumer__IMSHOW 1 // produce the images and log them, will not imshow\n// #define __Cerebro__loopcandi_consumer__IMSHOW 2 // produce the images and imshow them, don't log\n\n// Just uncomment it to disable consistency check.\n// #define __Cerebro__loopcandi_consumer__no_pose_consistency_check\nvoid Cerebro::loopcandiate_consumer_thread()\n{\n    assert( m_dataManager_available && \"You need to set the DataManager in class Cerebro before execution of the run() thread can begin. You can set the dataManager by call to Cerebro::setDataManager()\\n\");\n    assert( b_loopcandidate_consumer && \"you need to call loopcandidate_consumer_enable() before loopcandiate_consumer_thread() can start executing\\n\" );\n    cout << TermColor::GREEN() <<   \"[Cerebro::loopcandiate_consumer_thread] Start thread\" << TermColor::RESET() << endl;\n\n    // init StereoGeometry\n    bool stereogeom_status = init_stereogeom();\n    if( !stereogeom_status ) {\n        assert( false && \"[Cerebro::loopcandiate_consumer_thread()] cannot init_stereogeom\\n\");\n        return;\n    }\n    // stereogeom->set_K( 375.0, 375.0, 376.0, 240.0 ); // set this to something sensible, best is to use left camera's K\n\n\n    // init pt-feature-matcher\n\n\n    ros::Rate rate(1);\n    int prev_count = 0;\n    int new_count = 0;\n    ElapsedTime timer;\n\n    while( b_loopcandidate_consumer )\n    {\n        new_count = foundLoops_count();\n\n        if( new_count == prev_count ) {\n            rate.sleep();\n            continue;\n        }\n\n        __Cerebro__loopcandi_consumer__IMP(\n        cout << TermColor::iGREEN() ;\n        cout << \"I see \"<< new_count - prev_count << \" new candidates. from i=[\"<< prev_count << \",\" << new_count-1 << \"]\\n\";\n        // cout << \"Cerebro::loopcandiate_consumer_thread(); #loops=\" << new_count << TermColor::RESET() << endl;\n        cout << TermColor::RESET() ;\n        )\n\n        for( int j= prev_count ; j<=new_count-1 ; j++ )\n        {\n            ProcessedLoopCandidate proc_candi;\n\n            timer.tic() ;\n            #ifdef __Cerebro__loopcandi_consumer__no_pose_consistency_check\n            bool proc___status = process_loop_candidate_imagepair( j, proc_candi );\n            #else\n            bool proc___status = process_loop_candidate_imagepair_consistent_pose_compute( j, proc_candi );\n            #endif\n            __Cerebro__loopcandi_consumer__IMP(\n                cout << \"\\t\" << timer.toc_milli() << \"(ms) !! process_loop_candidate_imagepair()\\n\";\n            )\n\n            // the data is in the `proc_candi` which is put into a vector which is a class variable.\n            if( proc___status )\n            {\n                std::lock_guard<std::mutex> lk(m_processedLoops);\n\n                // publish proc_candi\n                cerebro::LoopEdge loopedge_msg;\n                #ifdef __Cerebro__loopcandi_consumer__no_pose_consistency_check\n                bool __makemsg_status = proc_candi.makeLoopEdgeMsg( loopedge_msg );\n                #else\n                bool __makemsg_status = proc_candi.makeLoopEdgeMsgWithConsistencyCheck( loopedge_msg );\n                #endif\n\n\n                __Cerebro__loopcandi_consumer__IMP(\n                cout << TermColor::iBLUE() <<  \"__makemsg_status: \" << __makemsg_status << \"  publish loopedge_msg\" << TermColor::RESET() << endl;\n                )\n\n                // publish only if msg was successfully made ==> all the candidate relative poses are consistent.\n                if( __makemsg_status )\n                {\n                    __Cerebro__loopcandi_consumer__IMP( cout << TermColor::GREEN() << \"publish loopedge_msg\" << TermColor::RESET() << endl; )\n                    pub_loopedge.publish( loopedge_msg );\n                } else {\n                    __Cerebro__loopcandi_consumer__IMP( cout << TermColor::RED() << \"not publishing because __makemsg_status was false\" << TermColor::RESET() << endl; )\n                }\n\n                // irrespective of where the poses are consistent or not add it to the list.\n                processedloopcandi_list.push_back(  proc_candi );\n                __Cerebro__loopcandi_consumer__IMP(cout  << \"\\tAdded to `processedloopcandi_list`\"  << endl;)\n            }\n            else {\n                __Cerebro__loopcandi_consumer__IMP( cout << \"\\tNot added to `processedloopcandi_list`, status was false\\n\" << endl; )\n            }\n        }\n\n        prev_count = new_count;\n        rate.sleep();\n\n    }\n\n    cout << TermColor::RED() <<  \"[Cerebro::loopcandiate_consumer_thread] disable called, quitting loopcandiate_consumer_thread\" << TermColor::RESET() << endl;\n\n}\n\n\nconst int Cerebro::processedLoops_count() const\n{\n    std::lock_guard<std::mutex> lk(m_processedLoops);\n    return processedloopcandi_list.size();\n\n}\n\nconst ProcessedLoopCandidate& Cerebro::processedLoops_i( int i ) const\n{\n    std::lock_guard<std::mutex> lk(m_processedLoops);\n\n    assert( i>=0 && i< processedloopcandi_list.size() && \"[processedLoops_i] input i is not in the correct range\" );\n    if( i>=0 && i< processedloopcandi_list.size() ) {\n        return processedloopcandi_list[ i ];\n    }\n    else {\n        cout << TermColor::RED() << \"[Cerebro::processedLoops_i] error you requested for processedloopcandidate idx=\" << i << \" but the length of the vector was \" << processedloopcandi_list.size() << endl;\n        exit(1);\n    }\n}\n\n\n\nbool Cerebro::init_stereogeom()\n{\n    auto left_camera = dataManager->getAbstractCameraRef(0);\n    if( !dataManager->isAbstractCameraSet(1) )\n    {\n        ROS_ERROR( \"Stereo-camera right doesn't appear to be set. to use this thread, you need to have StereoGeometry. Edit the config file to set the 2nd camera and its baseline\\n\");\n        return false;\n    }\n    auto right_camera = dataManager->getAbstractCameraRef(1);\n    __Cerebro__loopcandi_consumer__(\n    cout << TermColor::iGREEN() ;\n    cout << \"[Cerebro::loopcandiate_consumer_thread] left_camera\\n\" << left_camera->parametersToString() << endl;\n    cout << \"[Cerebro::loopcandiate_consumer_thread] right_camera\\n\" << right_camera->parametersToString() << endl;\n    cout << TermColor::RESET();\n    )\n\n    Matrix4d right_T_left;\n    if( !dataManager->isCameraRelPoseSet( std::make_pair(1,0)) )\n    {\n        ROS_ERROR( \"stereo camera appears to be set but the baseline (stereo-extrinsic) is not specified in the config_file. In config file you need to set `extrinsic_1_T_0`\");\n        return false;\n    }\n    right_T_left = dataManager->getCameraRelPose( std::make_pair(1,0) );\n    __Cerebro__loopcandi_consumer__(\n    cout << TermColor::iGREEN() ;\n    cout << \"[Cerebro::loopcandiate_consumer_thread] right_T_left: \" << PoseManipUtils::prettyprintMatrix4d( right_T_left ) << endl;\n    cout << TermColor::RESET();\n    )\n    stereogeom = std::make_shared<StereoGeometry>( left_camera,right_camera,     right_T_left  );\n    return true;\n}\n\n\nbool Cerebro::retrive_stereo_pair( DataNode* node, cv::Mat& left_image, cv::Mat& right_image, bool bgr2gray )\n{\n    #if 0\n    // raw stereo-images in gray\n    if( !(node->isImageAvailable()) || !(node->isImageAvailable(1)) ) {\n        cout << TermColor::RED() << \"[Cerebro::retrive_stereo_pair] Either of the node images (stereo-pair was not available). This is probably not fatal, this loopcandidate will be skipped.\" << TermColor::RESET() << endl;\n        return false;\n    }\n\n    const cv::Mat& bgr_left_image = node->getImage();\n    const cv::Mat& bgr_right_image = node->getImage(1);\n    #else\n    auto img_data_mgr = dataManager->getImageManagerRef();\n\n    if( img_data_mgr->isImageRetrivable( \"left_image\", node->getT() )==false )\n    {\n        cout << TermColor::RED() << \"[Cerebro::retrive_stereo_pair] img_data_mgr->isImageRetrivable( \\\"left_image\\\",\" << node->getT() << \") returned false\" << TermColor::RESET() << endl;\n        cout << TermColor::RED() << \"[Cerebro::retrive_stereo_pair] Either of the node images (stereo-pair was not available). This is probably not fatal, this loopcandidate will be skipped.\" << TermColor::RESET() << endl;\n        return false;\n    }\n\n    if (   img_data_mgr->isImageRetrivable( \"right_image\", node->getT() ) == false )\n    {\n        cout << TermColor::RED() << \"[Cerebro::retrive_stereo_pair] img_data_mgr->isImageRetrivable( \\\"right_image\\\",\" << node->getT() << \") returned false\" << TermColor::RESET() << endl;\n        cout << TermColor::RED() << \"[Cerebro::retrive_stereo_pair] Either of the node images (stereo-pair was not available). This is probably not fatal, this loopcandidate will be skipped.\" << TermColor::RESET() << endl;\n        // img_data_mgr->print_status();\n        // cout << TermColor::RED() << \"[Cerebro::retrive_stereo_pair] img_data_mgr->isImageRetrivable( \\\"right_image\\\",\" << node->getT() << \") returned false\" << TermColor::RESET() << endl;\n        return false;\n    }\n\n\n    cv::Mat bgr_left_image, bgr_right_image;\n    img_data_mgr->getImage( \"left_image\", node->getT(), bgr_left_image );\n    img_data_mgr->getImage( \"right_image\", node->getT(), bgr_right_image );\n\n            #if 0\n            cout << \"bgr_left_image: \" << MiscUtils::cvmat_info( bgr_left_image ) << \"\\n\";\n            cout << \"bgr_right_image: \" << MiscUtils::cvmat_info( bgr_right_image ) << \"\\n\";\n            #endif\n    if( !(bgr_left_image.data) || !(bgr_right_image.data) )\n    {\n         cout << \"[Cerebro::retrive_stereo_pair] Invalid images bfhjreturn false.\\n\";\n         return false;\n    }\n    #endif\n\n    if( bgr2gray ) {\n\n        if( bgr_left_image.channels() > 1 )\n            cv::cvtColor( bgr_left_image, left_image, CV_BGR2GRAY );\n        else\n            left_image = bgr_left_image;\n\n        if( bgr_right_image.channels() > 1 )\n            cv::cvtColor( bgr_right_image, right_image, CV_BGR2GRAY );\n        else\n            right_image = bgr_right_image;\n    }\n    else {\n        left_image = bgr_left_image;\n        right_image = bgr_right_image;\n    }\n\n    #if 0\n    cout << \"[Cerebro::retrive_stereo_pair]left_image: \" << MiscUtils::cvmat_info( left_image ) << \"\\n\";\n    cout << \"[Cerebro::retrive_stereo_pair]right_image: \" << MiscUtils::cvmat_info( right_image ) << \"\\n\";\n    cout << endl;\n    #endif\n    return true;\n\n}\n\n\n\nbool Cerebro::process_loop_candidate_imagepair_consistent_pose_compute( int ii, ProcessedLoopCandidate& proc_candi )\n{\n    auto u = foundLoops_i( ii );\n    ros::Time t_curr = std::get<0>(u);\n    ros::Time t_prev = std::get<1>(u);\n    double score = std::get<2>(u);\n\n    auto data_map = dataManager->getDataMapRef();\n    assert( data_map->count( t_curr ) > 0 && data_map->count( t_prev ) > 0  && \"One or both of the timestamps in foundloops where not in the data_map. This cannot be happening...fatal...\\n\" );\n    int idx_1 = std::distance( data_map->begin(), data_map->find( t_curr )  );\n    int idx_2 = std::distance( data_map->begin(), data_map->find( t_prev )  );\n\n    DataNode * node_1 = data_map->find( t_curr )->second;\n    DataNode * node_2 = data_map->find( t_prev )->second;\n\n\n\n\n    __Cerebro__loopcandi_consumer__IMP(\n    cout << TermColor::BLUE() << \"{\"<<ii <<  \"} process: \"<< idx_1 << \"<--->\" << idx_2 << TermColor::RESET() << \"\\tt_curr=\" << t_curr << \" <--> t_prev=\" << t_prev << endl;\n    )\n\n    // if( node_1->getNumberOfSuccessfullyTrackedFeatures() < 20 || node_2->getNumberOfSuccessfullyTrackedFeatures() < 20 ) {\n        // __Cerebro__loopcandi_consumer__IMP( \"[Cerebro::process_loop_candidate_imagepair_consistent_pose_compute] skip processing this pair because it is a kidnaped node\\n\" );\n        // return false;\n    // }\n\n    cv::Mat a_imleft_raw, a_imright_raw;\n    bool ret_status_a = retrive_stereo_pair( node_1, a_imleft_raw, a_imright_raw );\n    if( !ret_status_a )\n        return false;\n\n    cv::Mat b_imleft_raw, b_imright_raw;\n    bool ret_status_b = retrive_stereo_pair( node_2, b_imleft_raw, b_imright_raw );\n    if( !ret_status_b )\n        return false;\n\n    //------------------------------------------------\n    //------ 3d points from frame_a\n    //------------------------------------------------\n    __Cerebro__loopcandi_consumer__( cout << \"[Cerebro::process_loop_candidate_imagepair_consistent_pose_compute] 3d points from frame_a\\n\"; )\n    cv::Mat a_imleft_srectified, a_imright_srectified;\n    cv::Mat a_3dImage;\n    MatrixXd a_3dpts;\n    cv::Mat a_disp_viz;\n    stereogeom->get_srectifiedim_and_3dpoints_and_3dmap_and_disparity_from_raw_images(\n        a_imleft_raw, a_imright_raw,\n        a_imleft_srectified,a_imright_srectified,  a_3dpts, a_3dImage, a_disp_viz );\n\n\n    //-----------------------------------------------------\n    //--------------- 3d points from frame_b\n    //-----------------------------------------------------\n    __Cerebro__loopcandi_consumer__( cout << \"[Cerebro::process_loop_candidate_imagepair_consistent_pose_compute] 3d points from frame_b\\n\"; )\n    cv::Mat b_imleft_srectified, b_imright_srectified;\n    cv::Mat b_3dImage;\n    MatrixXd b_3dpts;\n    cv::Mat b_disp_viz;\n    stereogeom->get_srectifiedim_and_3dpoints_and_3dmap_and_disparity_from_raw_images(\n        b_imleft_raw, b_imright_raw,\n        b_imleft_srectified, b_imright_srectified,  b_3dpts, b_3dImage, b_disp_viz );\n\n\n    //---------------------------------------------------------------------\n    //------------ point matches between a_left, b_left\n    //---------------------------------------------------------------------\n    __Cerebro__loopcandi_consumer__( cout << \"[Cerebro::process_loop_candidate_imagepair_consistent_pose_compute]point matches between a_left, b_left\\n\"; )\n    MatrixXd uv, uv_d; // u is from frame_a; ud is from frame_b\n    ElapsedTime timer;\n    timer.tic();\n    StaticPointFeatureMatching::gms_point_feature_matches(a_imleft_srectified, b_imleft_srectified, uv, uv_d );\n    string msg_pf_matches = to_string( timer.toc_milli() )+\" (ms) elapsed time for point_feature_matches computation and resulted in \" + std::to_string(uv.cols()) + \" number of point correspondences\" ;\n    __Cerebro__loopcandi_consumer__( cout << msg_pf_matches << endl; )\n    if( uv.cols() < 150 ) {\n        __Cerebro__loopcandi_consumer__IMP(\n        cout << TermColor::RED() << \"[Cerebro::process_loop_candidate_imagepair_consistent_pose_compute]too few gms matches between node#\" << idx_1 << \" and node#\" << idx_2;\n        cout << \" contains pf_matches=\" << uv.cols() << \", thresh=150, so I am rejecting this loopcandidate.\" << TermColor::RESET() << endl;\n        )\n        return false;\n    }\n\n\n\n\n    //----------------------------------------------------------------------\n    //-------------- PNP and P3P\n    //----------------------------------------------------------------------\n    Matrix4d odom_b_T_a = node_2->getPose().inverse() * node_1->getPose();\n    proc_candi = ProcessedLoopCandidate( ii, node_1, node_2 );\n    proc_candi.idx_from_datamanager_1 = idx_1;\n    proc_candi.idx_from_datamanager_2 = idx_2;\n    proc_candi.pf_matches = uv.cols();\n\n    //----- Option-A:\n    __Cerebro__loopcandi_consumer__IMP( cout << TermColor::BLUE() << \"Option-A\" << TermColor::RESET() << endl; )\n    std::vector<Eigen::Vector2d> feature_position_uv;\n    std::vector<Eigen::Vector2d> feature_position_uv_d;\n    std::vector<Eigen::Vector3d> world_point_uv;\n    StaticPointFeatureMatching::make_3d_2d_collection__using__pfmatches_and_disparity(\n        stereogeom, uv, a_3dImage, uv_d,\n                                feature_position_uv, feature_position_uv_d, world_point_uv);\n    Matrix4d op1__b_T_a; string pnp__msg;\n    #if 1\n    //--\n    float pnp_goodness = StaticTheiaPoseCompute::PNP( world_point_uv, feature_position_uv_d, op1__b_T_a, pnp__msg  );\n    //--END\n    #else\n    //--\n    op1__b_T_a = odom_b_T_a; // setting initial guess as odometry rel pose with translation as zero\n    // op1__b_T_a(0,3) = 0.0; op1__b_T_a(1,3) = 0.0; op1__b_T_a(2,3) = 0.0;\n    float pnp_goodness = StaticCeresPoseCompute::PNP( world_point_uv, feature_position_uv_d, op1__b_T_a, pnp__msg  );\n    //--END\n    #endif\n    __Cerebro__loopcandi_consumer__IMP(\n    cout << TermColor::YELLOW() << \"pnp_goodness=\" << pnp_goodness << \" op1__b_T_a = \" << PoseManipUtils::prettyprintMatrix4d( op1__b_T_a ) << TermColor::RESET() << endl;\n    )\n    __Cerebro__loopcandi_consumer__(\n    cout << pnp__msg << endl;\n    )\n\n    // reprojection debug image for op-1\n    // plot( PI( op1__b_T_a * world_point_uv ) ) on imB\n    #if (__Cerebro__loopcandi_consumer__IMSHOW == 1) || (__Cerebro__loopcandi_consumer__IMSHOW == 2)\n    {\n    MatrixXd PI_world_point_uv, PI_world_point_uv_odom;\n    GeometryUtils::idealProjection( stereogeom->get_K(), op1__b_T_a, world_point_uv, PI_world_point_uv );\n    GeometryUtils::idealProjection( stereogeom->get_K(), odom_b_T_a, world_point_uv, PI_world_point_uv_odom );\n    cv::Mat pi_dst_img;\n    MiscUtils::plot_point_sets( b_imleft_srectified, PI_world_point_uv, pi_dst_img, cv::Scalar(0,255,0), false  );\n    MiscUtils::plot_point_sets( pi_dst_img, PI_world_point_uv_odom, cv::Scalar(255,0,0), false  );\n    MiscUtils::plot_point_sets( pi_dst_img, uv, cv::Scalar(0,0,255), false  );\n    MiscUtils::plot_point_sets( pi_dst_img, uv_d, cv::Scalar(255,0,255), false  );\n    cv::resize(pi_dst_img,pi_dst_img, cv::Size(), 0.6, 0.6 );\n    MiscUtils::append_status_image( pi_dst_img, string( \"^this is image b=\")+to_string(idx_2)+\";plot( PI( op1__b_T_a * world_point_uv ) ) in green on imB;plot( PI( odom__b_T_a * world_point_uv ) ) in blue on imB;plot( uv ) in red on imB;plot( uv_d) in pink on imB;>> green and pink show alignment quality; >> ignore blue and red\");\n\n\n    #if __Cerebro__loopcandi_consumer__IMSHOW == 1\n    proc_candi.debug_images.push_back( pi_dst_img );\n    proc_candi.debug_images_titles.push_back( \"reprojection_debug_image_1\" );\n    #endif\n\n    #if __Cerebro__loopcandi_consumer__IMSHOW == 2\n    cv::imshow( \"plot( PI( op1__b_T_a * world_point_uv ) ) on imB\", pi_dst_img );\n    #endif\n    }\n\n    #endif\n\n\n    //----- Option-B:\n    __Cerebro__loopcandi_consumer__IMP( cout << TermColor::BLUE() << \"Option-B\" << TermColor::RESET() << endl; )\n    std::vector<Eigen::Vector3d> world_point_uv_d;\n    StaticPointFeatureMatching::make_3d_2d_collection__using__pfmatches_and_disparity(\n        stereogeom, uv_d, b_3dImage, uv,\n                                feature_position_uv_d, feature_position_uv, world_point_uv_d);\n    Matrix4d op2__a_T_b, op2__b_T_a; string pnp__msg_option_B;\n    #if 1\n    //--\n    float pnp_goodness_optioN_B = StaticTheiaPoseCompute::PNP( world_point_uv_d, feature_position_uv, op2__a_T_b, pnp__msg_option_B  );\n    //--END\n    #else\n    //--\n    op2__a_T_b = odom_b_T_a.inverse();  //initial guess same as odometry\n    // op2__a_T_b(0,3)=0.0;op2__a_T_b(1,3)=0.0;op2__a_T_b(2,3)=0.0;\n    float pnp_goodness_optioN_B = StaticCeresPoseCompute::PNP( world_point_uv_d, feature_position_uv, op2__a_T_b, pnp__msg_option_B  );\n    //--END\n    #endif\n\n    op2__b_T_a = op2__a_T_b.inverse();\n    __Cerebro__loopcandi_consumer__IMP(\n    cout << TermColor::YELLOW() << pnp_goodness_optioN_B << \" op2__a_T_b = \" << PoseManipUtils::prettyprintMatrix4d( op2__a_T_b ) << TermColor::RESET() << endl;\n    cout << TermColor::YELLOW() << pnp_goodness_optioN_B << \" op2__b_T_a = \" << PoseManipUtils::prettyprintMatrix4d( op2__b_T_a ) << TermColor::RESET() << endl;\n    )\n    __Cerebro__loopcandi_consumer__(\n    cout << pnp__msg_option_B << endl;\n    )\n\n\n    // reprojection debug image for op-2\n    // plot( PI( op2__a_T_b * world_point_uv_d ) ) on imA\n    #if (__Cerebro__loopcandi_consumer__IMSHOW == 1) || (__Cerebro__loopcandi_consumer__IMSHOW == 2)\n    {\n    MatrixXd PI_world_point_uvd, PI_world_point_uvd_odom;\n    GeometryUtils::idealProjection( stereogeom->get_K(), op2__a_T_b, world_point_uv_d, PI_world_point_uvd );\n    GeometryUtils::idealProjection( stereogeom->get_K(), odom_b_T_a.inverse(), world_point_uv_d, PI_world_point_uvd_odom );\n    cv::Mat pi_dst_img;\n    MiscUtils::plot_point_sets( a_imleft_srectified, PI_world_point_uvd, pi_dst_img, cv::Scalar(0,255,0), false  );\n    MiscUtils::plot_point_sets( pi_dst_img, PI_world_point_uvd_odom, cv::Scalar(255,0,0), false  );\n    MiscUtils::plot_point_sets( pi_dst_img, uv, cv::Scalar(0,0,255), false  );\n    MiscUtils::plot_point_sets( pi_dst_img, uv_d, cv::Scalar(255,0,255), false  );\n    cv::resize(pi_dst_img,pi_dst_img, cv::Size(), 0.6, 0.6 );\n    MiscUtils::append_status_image( pi_dst_img, string( \"^this is image a=\")+to_string(idx_1)+\";plot( PI( op2__a_T_b * world_point_uv_d ) ) in green on imA;plot( PI( odom__b_T_a * world_point_uv ) ) in blue on imA ;plot( uv ) in red on imA;plot( uv_d) in pink on imA;>> green and red show alignment quality;>> ignore blue and pink\");\n\n\n    #if __Cerebro__loopcandi_consumer__IMSHOW == 1\n    proc_candi.debug_images.push_back( pi_dst_img );\n    proc_candi.debug_images_titles.push_back( \"reprojection_debug_image_2\" );\n    #endif\n\n    #if __Cerebro__loopcandi_consumer__IMSHOW == 2\n    cv::imshow( \"plot( PI( op2__a_T_b * world_point_uv_d ) ) on imA\", pi_dst_img );\n    #endif\n    }\n\n    #endif\n\n    //----- Option-C\n    __Cerebro__loopcandi_consumer__IMP( cout << TermColor::BLUE() << \"Option-C\" << TermColor::RESET() << endl; )\n    vector< Vector3d> uv_X;\n    vector< Vector3d> uvd_Y;\n    StaticPointFeatureMatching::make_3d_3d_collection__using__pfmatches_and_disparity( uv, a_3dImage, uv_d, b_3dImage,\n        uv_X, uvd_Y );\n    Matrix4d icp_b_T_a; string p3p__msg;\n    #if 1\n    //--\n    float p3p_goodness = StaticTheiaPoseCompute::P3P_ICP( uv_X, uvd_Y, icp_b_T_a, p3p__msg );\n    //--END\n    #else\n    //--\n    icp_b_T_a = odom_b_T_a;\n    // icp_b_T_a(0,3) = 0.0; icp_b_T_a(1,3) = 0.0; icp_b_T_a(2,3) = 0.0;\n    float p3p_goodness = StaticCeresPoseCompute::P3P_ICP( uv_X, uvd_Y, icp_b_T_a, p3p__msg );\n\n    //--END\n    #endif\n\n\n    __Cerebro__loopcandi_consumer__IMP(\n    cout << TermColor::YELLOW() << p3p_goodness << \" icp_b_T_a = \" << PoseManipUtils::prettyprintMatrix4d( icp_b_T_a ) << TermColor::RESET() << endl;\n    )\n    __Cerebro__loopcandi_consumer__(\n    cout << p3p__msg << endl;\n    )\n\n\n    // reprojection debug image for op-3\n    // plot( PI( icp_b_T_a * world_point_uv ) ) on imB\n    #if (__Cerebro__loopcandi_consumer__IMSHOW == 1) || (__Cerebro__loopcandi_consumer__IMSHOW == 2)\n    {\n    MatrixXd PI_world_point_uv, PI_world_point_uv_odom;\n    GeometryUtils::idealProjection( stereogeom->get_K(), icp_b_T_a, world_point_uv, PI_world_point_uv );\n    GeometryUtils::idealProjection( stereogeom->get_K(), odom_b_T_a, world_point_uv, PI_world_point_uv_odom );\n    cv::Mat pi_dst_img;\n    MiscUtils::plot_point_sets( b_imleft_srectified, PI_world_point_uv, pi_dst_img, cv::Scalar(0,255,0), false  );\n    MiscUtils::plot_point_sets( pi_dst_img, PI_world_point_uv_odom, cv::Scalar(255,0,0), false  );\n    MiscUtils::plot_point_sets( pi_dst_img, uv, cv::Scalar(0,0,255), false  );\n    MiscUtils::plot_point_sets( pi_dst_img, uv_d, cv::Scalar(255,0,255), false  );\n    cv::resize(pi_dst_img,pi_dst_img, cv::Size(), 0.6, 0.6 );\n    MiscUtils::append_status_image( pi_dst_img, string( \"^this is image b=\")+to_string(idx_2)+\";plot( PI( icp_b_T_a * world_point_uv ) ) in green on imB ;plot( PI( odom__b_T_a * world_point_uv ) ) in blue on imB;plot( uv ) in red on imB;plot( uv_d) in pink on imB\");\n\n\n    #if __Cerebro__loopcandi_consumer__IMSHOW == 1\n    proc_candi.debug_images.push_back( pi_dst_img );\n    proc_candi.debug_images_titles.push_back( \"reprojection_debug_image_3\" );\n    #endif\n\n    #if __Cerebro__loopcandi_consumer__IMSHOW == 2\n    cv::imshow( \"plot( PI( icp_b_T_a * world_point_uv ) ) on imB\", pi_dst_img );\n    #endif\n    }\n\n    #endif\n\n    // if( isnan( op1__b_T_a  ) ) {\n    if( op1__b_T_a != op1__b_T_a  || op2__b_T_a != op2__b_T_a || icp_b_T_a != icp_b_T_a ) {\n        __Cerebro__loopcandi_consumer__IMP( cout << \"=======++++ one of the 3 ways had inf or nan in them. This is bad, so skip this loop candidate.\\n\"; );\n        return false;\n    }\n\n    __Cerebro__loopcandi_consumer__IMP(\n    cout << TermColor::YELLOW() << \"odom_b_T_a = \" << PoseManipUtils::prettyprintMatrix4d( odom_b_T_a ) << TermColor::RESET() << endl;\n    cout << \"|op1 - op2|\" << PoseManipUtils::prettyprintMatrix4d( op1__b_T_a.inverse() * op2__b_T_a ) << endl;\n    cout << \"|op1 - icp|\" << PoseManipUtils::prettyprintMatrix4d( op1__b_T_a.inverse() * icp_b_T_a ) << endl;\n    cout << \"|op2 - icp|\" << PoseManipUtils::prettyprintMatrix4d( op2__b_T_a.inverse() * icp_b_T_a ) << endl;\n    )\n\n\n\n\n\n    //-----------------------------------------------------------------\n    // Fill the output\n    //-----------------------------------------------------------------\n\n    // final pose\n    // proc_candi._3d2d__2T1 = op1__b_T_a;\n    // proc_candi.isSet_3d2d__2T1 = true;\n    // proc_candi._3d2d__2T1__ransac_confidence = pnp_goodness;\n\n\n    // Fill up all the poses which were computed\n    // option-A\n    proc_candi.opX_b_T_a.push_back( op1__b_T_a );\n    proc_candi.opX_goodness.push_back( pnp_goodness );\n    proc_candi.opX_b_T_a_name.push_back( \"op1__b_T_a\" );\n    proc_candi.opX_b_T_a_debugmsg.push_back( pnp__msg );\n    // Option-B\n    proc_candi.opX_b_T_a.push_back( op2__b_T_a );\n    proc_candi.opX_goodness.push_back( pnp_goodness_optioN_B );\n    proc_candi.opX_b_T_a_name.push_back( \"op2__b_T_a\" );\n    proc_candi.opX_b_T_a_debugmsg.push_back( pnp__msg_option_B );\n    // Option-C\n    proc_candi.opX_b_T_a.push_back( icp_b_T_a );\n    proc_candi.opX_goodness.push_back( p3p_goodness );\n    proc_candi.opX_b_T_a_name.push_back( \"icp_b_T_a\" );\n    proc_candi.opX_b_T_a_debugmsg.push_back( p3p__msg );\n\n\n\n\n\n\n    //-----------------------------------------------------------------\n    // Build Viz Images\n    //-----------------------------------------------------------------\n    #if (__Cerebro__loopcandi_consumer__IMSHOW == 1) || (__Cerebro__loopcandi_consumer__IMSHOW == 2)\n\n        ///------A : pf matching\n    cv::Mat dst_feat_matches;\n    MiscUtils::plot_point_pair( a_imleft_srectified, uv, idx_1,\n                     b_imleft_srectified, uv_d, idx_2,\n                        dst_feat_matches, 3, msg_pf_matches+\";#pf-matches: \"+to_string( uv.cols() )  );\n    cv::resize(dst_feat_matches, dst_feat_matches, cv::Size(), 0.5, 0.5 );\n\n\n        ///----------B : Disparities\n    cv::Mat dst_disp;\n    MiscUtils::side_by_side( a_disp_viz, b_disp_viz, dst_disp );\n    MiscUtils::append_status_image( dst_disp, \"a=\"+ to_string(idx_1)+\"     b=\"+to_string(idx_2), .8 );\n    cv::resize(dst_disp, dst_disp, cv::Size(), 0.5, 0.5 );\n    // cv::imshow( \"dst_disp\", dst_disp );\n\n    MiscUtils::append_status_image( dst_disp, \"[b_T_a <-- PNP( 3d(a), uv(b))];\"+pnp__msg+\";[a_T_b <-- PNP( 3d(b), uv(a))];\"+pnp__msg_option_B+\";[icp_b_T_a <-- ICP(3d(a), 3d(b))];\"+p3p__msg );\n\n    MiscUtils::append_status_image( dst_disp, \"odom_b_T_a:\"+PoseManipUtils::prettyprintMatrix4d( odom_b_T_a ) );\n\n        ///---------C: Reprojections\n        //TODO\n\n    #if __Cerebro__loopcandi_consumer__IMSHOW == 1\n    proc_candi.debug_images.push_back( dst_feat_matches );\n    proc_candi.debug_images_titles.push_back( \"apf_matches\" );\n    proc_candi.debug_images.push_back( dst_disp );\n    proc_candi.debug_images_titles.push_back( \"dsparity_and_poses\" );\n    #endif\n\n    #if __Cerebro__loopcandi_consumer__IMSHOW == 2\n    cv::imshow( \"dst_feat_matches\", dst_feat_matches );\n    cv::imshow( \"dst_disp\", dst_disp );\n    cv::waitKey(30);\n    #endif\n\n    #endif\n\n\n    return true;\n\n}\n\n#if 0\nbool Cerebro::process_loop_candidate_imagepair( int ii, ProcessedLoopCandidate& proc_candi )\n{\n    auto u = foundLoops_i( ii );\n    ros::Time t_curr = std::get<0>(u);\n    ros::Time t_prev = std::get<1>(u);\n    double score = std::get<2>(u);\n\n    auto data_map = dataManager->getDataMapRef();\n    assert( data_map->count( t_curr ) > 0 && data_map->count( t_prev ) > 0  && \"One or both of the timestamps in foundloops where not in the data_map. This cannot be happening...fatal...\\n\" );\n    int idx_1 = std::distance( data_map->begin(), data_map->find( t_curr )  );\n    int idx_2 = std::distance( data_map->begin(), data_map->find( t_prev )  );\n\n    DataNode * node_1 = data_map->find( t_curr )->second;\n    DataNode * node_2 = data_map->find( t_prev )->second;\n\n\n    __Cerebro__loopcandi_consumer__IMP(\n    cout << TermColor::BLUE() << \"{\"<<ii <<  \"} process: \"<< idx_1 << \"<--->\" << idx_2 << TermColor::RESET() << endl;\n    )\n    // cv::imshow( \"im_1\", im_1);\n    // cv::imshow( \"im_2\", im_2);\n\n\n\n    //----------------------------------------\n    //---------Disparity of `idx_1`\n    //----------------------------------------\n    cv::Mat grey_im_1_left, grey_im_1_right;\n    bool ret_status = retrive_stereo_pair( node_1, grey_im_1_left, grey_im_1_right );\n    if( !ret_status )\n        return false;\n\n\n    // will get 3d points, stereo-rectified image, and disparity false colormap\n    MatrixXd _3dpts__im1; //4xN. 3d points of im1\n    cv::Mat _3dImage__im1;\n    cv::Mat imleft_srectified, imright_srectified;\n    cv::Mat disparity_for_visualization;\n    ElapsedTime timer;\n    timer.tic();\n    stereogeom->get_srectifiedim_and_3dpoints_and_3dmap_and_disparity_from_raw_images( grey_im_1_left, grey_im_1_right,\n         imleft_srectified,imright_srectified,\n         _3dpts__im1, _3dImage__im1, disparity_for_visualization );\n    __Cerebro__loopcandi_consumer__(\n    cout << timer.toc_milli() << \" (ms)!!  get_srectifiedim_and_3dpoints_and_disparity_from_raw_images\\n\";\n    )\n\n\n\n\n\n    //---------------- END Disparity of `idx_1`\n\n\n\n    //--------------------------------------------------------------\n    //------------ srectified of idx_2 image pair needed\n    //--------------------------------------------------------------\n    cv::Mat grey_im_2_left, grey_im_2_right;\n    bool ret_status_2 = retrive_stereo_pair( node_2, grey_im_2_left, grey_im_2_right );\n    if( !ret_status_2 )\n        return false;\n\n    cv::Mat im2_left_srectified, im2_right_srectified;\n    timer.tic();\n    // TODO: If feasible can also compute depth of im2, so that the pose computed can be verified for consistency.\n    // TODO: accept the pose if pnp( 3d of idx_1, 2d of idx_2 ) === pnp( 2d of idx_1, 3d of idx_2 )\n    stereogeom->do_stereo_rectification_of_raw_images( grey_im_2_left, grey_im_2_right,\n                            im2_left_srectified, im2_right_srectified );\n    __Cerebro__loopcandi_consumer__(\n    cout << timer.toc_milli() << \" (ms)!! do_stereo_rectification_of_raw_images\\n\";\n    )\n    // cv::imshow( \"im2_left_srectified\", im2_left_srectified );\n\n    //------------- END srectified of idx_2 image pair needed\n\n\n\n    //-----------------------------------------------------------------------\n    //----------point_feature_matches for `idx_1` <--> `idx_2`\n    // gms matcher\n    //-----------------------------------------------------------------------\n    MatrixXd uv, uv_d; // u is from frame_a; ud is from frame_b\n    timer.tic();\n    StaticPointFeatureMatching::gms_point_feature_matches( imleft_srectified, im2_left_srectified, uv, uv_d );\n    auto elapsed_time_gms = timer.toc_milli();\n    __Cerebro__loopcandi_consumer__(\n    cout << elapsed_time_gms << \" (ms)!! gms_point_feature_matches\\n\";\n    )\n\n\n    //--------------------- END Matcher ----------------------------------\n\n\n\n    //---------------- make collection of 3d 2d points\n    // 3d of `idx_1` <---> 2d of `idx_2`\n\n\n    std::vector<Eigen::Vector2d> feature_position_uv;\n    std::vector<Eigen::Vector2d> feature_position_uv_d;\n    std::vector<Eigen::Vector3d> world_point;\n\n\n    StaticPointFeatureMatching::make_3d_2d_collection__using__pfmatches_and_disparity( stereogeom, uv, _3dImage__im1,     uv_d,\n                                feature_position_uv, feature_position_uv_d, world_point );\n    // make_3d_2d_collection__using__pfmatches_and_disparity( uv, _3dImage__im1,     uv_d,\n                                // feature_position_uv, feature_position_uv_d, world_point );\n    int n_valid_depths = world_point.size();\n\n\n\n\n    // TODO 2d of idx_1 <---> 3d of idx_2\n\n\n\n\n\n    // See if the matching can be rejected due to insufficient # of matches\n    if( uv.cols() < 150 ) {\n        __Cerebro__loopcandi_consumer__IMP(\n        cout << TermColor::YELLOW() << \"of the total \"+std::to_string(uv.cols())+\" point feature correspondences \" +std::to_string(n_valid_depths)+ \" had valid depths. \" << \" too few pf-matches from gms. Skip this. TH=80\" << TermColor::RESET() << endl;\n        )\n        return false;\n    }\n\n    if( n_valid_depths < 150 ) {\n        __Cerebro__loopcandi_consumer__IMP(\n        cout << TermColor::YELLOW() << \"of the total \"+std::to_string(uv.cols())+\" point feature correspondences \" +std::to_string(n_valid_depths)+ \" had valid depths. \" << \"too few pf-matches depths from gms. Skip this. TH=80\" << TermColor::RESET() << endl;\n        )\n        return false;\n    }\n\n    // TODO assess u, ud, world_point for histogram spreads. a more spread in measurements will give more precise more. Also will be helpful to weedout bad poses\n\n    //------------------------------------------\n    //-------------- theia::pnp\n    //------------------------------------------\n    Matrix4d b_T_a; //< RESULT\n    string pnp__msg = string(\"\"); //< msg about pnp\n    #if 1\n    //--- DlsPnp\n    std::vector<Eigen::Quaterniond> solution_rotations;\n    std::vector<Eigen::Vector3d> solution_translations;\n    timer.tic();\n    theia::DlsPnp( feature_position_uv_d, world_point, &solution_rotations, &solution_translations  );\n    auto elapsed_dls_pnp = timer.toc_milli() ;\n    __Cerebro__loopcandi_consumer__(\n    cout << elapsed_dls_pnp << \" : (ms) : theia::DlsPnp done in\\n\";\n    cout << \"solutions count = \" << solution_rotations.size() << \" \" << solution_translations.size() << endl;\n    )\n\n    if( solution_rotations.size() == 0 ) {\n        __Cerebro__loopcandi_consumer__IMP(\n        cout << TermColor::RED() << \" theia::DlsPnp returns no solution\" << TermColor::RESET() << endl;\n        )\n        return false;\n    }\n\n    if( solution_rotations.size() > 1 ) {\n        __Cerebro__loopcandi_consumer__IMP(\n        cout << TermColor::RED() << \" theia::DlsPnp returns multiple solution\" << TermColor::RESET() << endl;\n        )\n        return false;\n    }\n\n    // retrive solution\n    b_T_a = Matrix4d::Identity();\n    b_T_a.topLeftCorner(3,3) = solution_rotations[0].toRotationMatrix();\n    b_T_a.col(3).topRows(3) = solution_translations[0];\n\n    __Cerebro__loopcandi_consumer__IMP(\n    // cout << \"solution_T \" << b_T_a << endl;\n    cout << TermColor::GREEN() << \"DlsPnp (b_T_a): \" << PoseManipUtils::prettyprintMatrix4d( b_T_a ) << TermColor::RESET() << endl;\n    // out_b_T_a = b_T_a;\n    pnp__msg +=  \"DlsPnp (b_T_a): \" + PoseManipUtils::prettyprintMatrix4d( b_T_a ) + \";\";\n    pnp__msg += \"  elapsed_dls_pnp=\"+to_string(elapsed_dls_pnp)+\";\";\n    )\n    #endif\n\n\n    #if 1\n    //--- DlsPnpWithRansac\n    __Cerebro__loopcandi_consumer__( timer.tic() );\n    // prep data\n    vector<CorrespondencePair_3d2d> data_r;\n    for( int i=0 ; i<world_point.size() ; i++ )\n    {\n        CorrespondencePair_3d2d _data;\n        _data.a_X = world_point[i];\n        _data.uv_d = feature_position_uv_d[i];\n        data_r.push_back( _data );\n    }\n\n    // Specify RANSAC parameters.\n\n    DlsPnpWithRansac dlspnp_estimator;\n    RelativePose best_rel_pose;\n\n    // Set the ransac parameters.\n    theia::RansacParameters params;\n    params.error_thresh = 0.02;\n    params.min_inlier_ratio = 0.7;\n    params.max_iterations = 50;\n    params.min_iterations = 5;\n    params.use_mle = true;\n\n    // Create Ransac object, specifying the number of points to sample to\n    // generate a model estimation.\n    theia::Ransac<DlsPnpWithRansac> ransac_estimator(params, dlspnp_estimator);\n    // Initialize must always be called!\n    ransac_estimator.Initialize();\n\n    theia::RansacSummary summary;\n    ransac_estimator.Estimate(data_r, &best_rel_pose, &summary);\n\n    auto elapsed_dls_pnp_ransac=timer.toc_milli();\n    __Cerebro__loopcandi_consumer__IMP(\n    cout << elapsed_dls_pnp_ransac << \"(ms)!! DlsPnpWithRansac ElapsedTime includes data prep\\n\";\n    cout << \"Ransac:\";\n    // for( int i=0; i<summary.inliers.size() ; i++ )\n        // cout << \"\\t\" << i<<\":\"<< summary.inliers[i];\n    cout << \"\\tnum_iterations=\" << summary.num_iterations;\n    cout << \"\\tconfidence=\" << summary.confidence;\n    cout << endl;\n    cout << TermColor::GREEN() << \"best solution (ransac) : \"<< PoseManipUtils::prettyprintMatrix4d( best_rel_pose.b_T_a ) << TermColor::RESET() << endl;\n    )\n    pnp__msg +=  \"DlsPnpWithRansac (best_rel_pose.b_T_a): \" + PoseManipUtils::prettyprintMatrix4d( best_rel_pose.b_T_a ) + \";\";\n    pnp__msg += string(\"    num_iterations=\")+to_string(summary.num_iterations)+\"  confidence=\"+to_string(summary.confidence);\n    pnp__msg += string( \"   elapsed_dls_pnp_ransac (ms)=\")+to_string(elapsed_dls_pnp_ransac)+\";\";\n\n\n    b_T_a = best_rel_pose.b_T_a;\n    #endif\n\n\n\n\n    __Cerebro__loopcandi_consumer__(\n    // cout << \"solution_T \" << b_T_a << endl;\n    cout << \"Final (b_T_a): \" << PoseManipUtils::prettyprintMatrix4d( b_T_a ) << endl;\n    // out_b_T_a = b_T_a;\n    )\n\n\n    // Fill the output\n    proc_candi = ProcessedLoopCandidate( ii, node_1, node_2 );\n    proc_candi.idx_from_datamanager_1 = idx_1;\n    proc_candi.idx_from_datamanager_2 = idx_2;\n    proc_candi.pf_matches = uv.cols();\n    proc_candi._3d2d_n_pfvalid_depth = n_valid_depths;\n    proc_candi._3d2d__2T1 = b_T_a;\n    proc_candi.isSet_3d2d__2T1 = true;\n    proc_candi._3d2d__2T1__ransac_confidence = summary.confidence;\n\n\n    //################ All Plotting/Viz/Debug after this ##########################//\n\n\n    //-----------------------------------------\n    // plot point-feature matches.\n    //------------------------------------------\n    #if (__Cerebro__loopcandi_consumer__IMSHOW == 1) || (__Cerebro__loopcandi_consumer__IMSHOW == 2)\n    cv::Mat dst_feat_matches;\n    string msg = string(\"gms_matcher:\")+\"of the total \"+std::to_string(uv.cols())+\" point feature correspondences \" +std::to_string(n_valid_depths)+ \" had valid depths;\"+\"computed in (ms)\"+to_string(elapsed_time_gms);\n    msg += \";_3d2d_2T1: \"+  PoseManipUtils::prettyprintMatrix4d( b_T_a ) ;\n    MiscUtils::plot_point_pair( imleft_srectified, uv, idx_1,\n                     im2_left_srectified, uv_d, idx_2,\n                        dst_feat_matches, 3, msg  );\n    cv::resize(dst_feat_matches, dst_feat_matches, cv::Size(), 0.5, 0.5 );\n\n    #if __Cerebro__loopcandi_consumer__IMSHOW == 2\n    cv::imshow( \"dst_feat_matches\", dst_feat_matches );\n    #endif\n\n\n\n    __Cerebro__loopcandi_consumer__(\n    string msg2 = string(\"gms_matcher:\")+\"of the total \"+std::to_string(uv.cols())+\" point feature correspondences \" +std::to_string(n_valid_depths)+ \" had valid depths;\"+\"computed in (ms)\"+to_string(elapsed_time_gms);\n    msg2 += \";_3d2d_2T1: \"+  PoseManipUtils::prettyprintMatrix4d( b_T_a ) ;\n    cout << msg2 << endl;\n    cout << \"adding `cv::Mat dst_feat_matches` to `proc_candi.matching_im_pair`\\n\";\n    )\n    // proc_candi.matching_im_pair = dst_feat_matches;\n    proc_candi.debug_images.push_back( dst_feat_matches );\n    proc_candi.debug_images_titles.push_back( \"matching_im_pair\");\n\n    #endif\n\n\n\n    // verification image of the pose\n    #if (__Cerebro__loopcandi_consumer__IMSHOW == 1) || (__Cerebro__loopcandi_consumer__IMSHOW == 2)\n    //---------------------------------------------\n    //--- Use the theia's estimated pose do PI( b_T_a * 3dpts ) and plot these\n    //--- points on B. Also plot detected points of B\n    //---------------------------------------------\n    MatrixXd _3dpts_of_A_projectedonB = MatrixXd::Zero(3, world_point.size() );\n    MatrixXd _detectedpts_of_B = MatrixXd::Zero(3, world_point.size() );\n    MatrixXd _detectedpts_of_A = MatrixXd::Zero(3, world_point.size() );\n    int n_good = 0;\n    for( int i=0 ; i<world_point.size() ; i++ ) {\n        Vector4d a_X_i;\n        a_X_i << Vector3d(world_point[i]), 1.0;\n        Vector4d b_X_i = b_T_a * a_X_i;\n        Vector3d _X_i = b_X_i.topRows(3);\n        _X_i /= _X_i(2); // Z-division for projection\n        _3dpts_of_A_projectedonB.col(i) = stereogeom->get_K() * _X_i; //< scaling with camera-intrinsic matrix\n\n\n        Vector3d _tmp;\n        _tmp << feature_position_uv_d[i], 1.0 ;\n        _detectedpts_of_B.col(i) = stereogeom->get_K() * _tmp;\n\n        _tmp << feature_position_uv[i], 1.0 ;\n        _detectedpts_of_A.col(i) = stereogeom->get_K() * _tmp;\n\n\n        Vector3d delta = _3dpts_of_A_projectedonB.col(i) - _detectedpts_of_B.col(i);\n        if( abs(delta(0)) < 2. && abs(delta(1)) < 2. ) {\n            // cout << \"  delta=\" << delta.transpose();\n            n_good++;\n        }\n        else {\n            // cout << TermColor::RED() << \"delta=\" << delta.transpose() << TermColor::RESET();\n        }\n        // cout << endl;\n    }\n    // cout << \"n_good=\" <<n_good << endl;\n\n\n    //------------------------------\n    //--- Use relative pose of obometry to do PI( odom_b_T_a * 3dpts ) and plot these on image-b\n    //--- load odometry poses\n    //-------------------------------\n    #if 1\n\n    MatrixXd _3dpts_of_A_projectedonB_with_odom_rel_pose = MatrixXd::Zero(3, world_point.size() );\n    if( node_1->isPoseAvailable() && node_2->isPoseAvailable() ) {\n\n        Matrix4d odom_wTA = node_1->getPose();\n        // cout << \"wTa(odom)\" << PoseManipUtils::prettyprintMatrix4d( wTa ) << endl;\n        Matrix4d odom_wTB = node_2->getPose();\n        // cout << \"wTb(odom)\" << PoseManipUtils::prettyprintMatrix4d( wTb ) << endl;\n        Matrix4d odom_b_T_a = odom_wTB.inverse() * odom_wTA;\n\n\n        __Cerebro__loopcandi_consumer__(\n            cout << \"odom_b_T_a\" << PoseManipUtils::prettyprintMatrix4d( odom_b_T_a ) << endl;\n        )\n        pnp__msg += \"odom_b_T_a \"+PoseManipUtils::prettyprintMatrix4d( odom_b_T_a ) +\";\";\n\n        auto a_timestamp = node_1->getT();\n        auto b_timestamp = node_2->getT();\n        pnp__msg += \"a.timestamp: \" + to_string(a_timestamp.toSec());\n        pnp__msg += \"    b.timestamp: \" + to_string(b_timestamp.toSec()) + \";\";\n\n        // PI( odom_b_T_a * a_X )\n        for( int i=0 ; i<world_point.size() ; i++ ) {\n            Vector4d a_X_i;\n            a_X_i << Vector3d(world_point[i]), 1.0;\n            Vector4d b_X_i = odom_b_T_a * a_X_i;\n            Vector3d _X_i = b_X_i.topRows(3);\n            _X_i /= _X_i(2); // Z-division for projection\n            _3dpts_of_A_projectedonB_with_odom_rel_pose.col(i) = stereogeom->get_K() * _X_i; //< scaling with camera-intrinsic matrix\n        }\n\n    }\n    else {\n        cout << TermColor::RED() << \"[Cerebro::process_loop_candidate_imagepair] In plotting part needs poses from DataManager which is not available.\" << TermColor::RESET() << endl;\n    }\n\n    #endif\n\n\n\n\n    // plot( B, ud )\n    cv::Mat __dst;\n    MiscUtils::plot_point_sets( im2_left_srectified, _detectedpts_of_B, __dst, cv::Scalar( 0,0,255), false, \"_detectedpts_of_B (red) npts=\"+to_string(_detectedpts_of_B.cols()) );\n    // cv::imshow( \"plot( B, ud )\", __dst );\n\n    // plot( B, _3dpts_of_A_projectedonB )\n    MiscUtils::plot_point_sets( __dst, _3dpts_of_A_projectedonB, cv::Scalar( 0,255,255), false, \";_3dpts_of_A_projectedonB, PI( b_T_a * X),(yellow)\" );\n    // MiscUtils::plot_point_sets( b_imleft_srectified, _3dpts_of_A_projectedonB, __dst, cv::Scalar( 0,255,255), false, \"_3dpts_of_A_projectedonB\" );\n\n    MiscUtils::plot_point_sets( __dst, _detectedpts_of_A, cv::Scalar( 255,255,255), false, \";;_detectedpts_of_A(white)\" );\n\n    MiscUtils::plot_point_sets( __dst, _3dpts_of_A_projectedonB_with_odom_rel_pose, cv::Scalar( 255,0,0), false, \";;;_3dpts_of_A_projectedonB_with_odom_rel_pose, PI( odom_b_T_a * X),(blue)\" );\n\n\n    // status image for __dst.\n    string status_msg = \"im2_left_srectified;\";\n    status_msg += \"_detectedpts_of_B (red);\";\n    status_msg += \"_3dpts_of_A_projectedonB, PI( b_T_a * X),(yellow);\";\n    status_msg += \"_detectedpts_of_A(white);\";\n    status_msg += \"_3dpts_of_A_projectedonB_with_odom_rel_pose, PI( odom_b_T_a * X),(blue);\";\n    MiscUtils::append_status_image( __dst, status_msg+\";\"+pnp__msg );\n\n    // proc_candi.pnp_verification_image = __dst;\n    proc_candi.debug_images.push_back( __dst );\n    proc_candi.debug_images_titles.push_back( \"pnp_verification_image\" );\n\n    #if __Cerebro__loopcandi_consumer__IMSHOW == 2\n    cv::imshow( \"im2_left_srectified\", __dst );\n    #endif\n\n    #endif\n\n\n\n    // disparity and other images\n    #if (__Cerebro__loopcandi_consumer__IMSHOW == 1) || (__Cerebro__loopcandi_consumer__IMSHOW == 2)\n    // proc_candi.node_1_disparity_viz = disparity_for_visualization; // this is disparity of im1. see code way above\n    proc_candi.debug_images.push_back( disparity_for_visualization );\n    proc_candi.debug_images_titles.push_back( \"node_1_disparity_viz\" );\n\n    #if __Cerebro__loopcandi_consumer__IMSHOW == 2\n    // cv::imshow( \"imleft_srectified\", imleft_srectified );\n    // cv::imshow( \"imright_srectified\", imright_srectified );\n    cv::imshow( \"im1_disparity_for_visualization\", disparity_for_visualization );\n    #endif\n    #endif\n\n\n\n    // Done\n    #if __Cerebro__loopcandi_consumer__IMSHOW == 2\n    cv::waitKey(50);\n    #endif\n\n    return true;\n\n\n\n\n\n}\n#endif\n//////////// END pose computation //////////////////////////////\n\n\n//--------------------------------------------------------------//\n//------------------ END Geometry Thread -----------------------//\n//--------------------------------------------------------------//\n\n\n\n///////////////////////////////////////////////////////////\n//---------------------- KIDNAP -------------------------//\n///////////////////////////////////////////////////////////\n\n// Kidnap identification thread. This thread monitors dataManager->getDataMapRef().size\n// for every new node added if there are zero tracked features means that, I have\n// been kidnaped. It however declares kidnap only after 2 sec of kidnaped\n// #define __Cerebro__kidnaped_thread__( msg ) msg;\n#define __Cerebro__kidnaped_thread__( msg ) ;\n\n// #define __Cerebro__kidnaped_thread__debug( msg ) msg;\n#define __Cerebro__kidnaped_thread__debug( msg ) ;\nvoid Cerebro::kidnaped_thread( int loop_rate_hz )\n{\n    if( loop_rate_hz <= 0 || loop_rate_hz >= 30 ) {\n        cout << TermColor::RED() << \"[Cerebro::kidnaped_thead] Invalid loop_rate_hz. Expected to be between [1,30]\\n\" << TermColor::RESET() << endl;\n        return;\n    }\n\n    cout << TermColor::GREEN() << \"Start  Cerebro::kipnaped_thead\\n\" << TermColor::RESET() << endl;\n\n\n    if( dataManager->isKidnapIndicatorPubSet() == false ) {\n        cout << TermColor::RED() << \"FATAL ERROR in [Cerebro::kidnaped_thread] kidnap indicator ros::Publishers were not set\\n\";\n        exit(2);\n    }\n\n\n    //---\n    //--- Main Settings for this thread\n    //---\n    const int THRESH_N_FEATS = 15; //declare kidnap if number of tracked features fall below 15\n    const ros::Duration WAIT_BEFORE_DECLARING_AS_KIDNAP = ros::Duration(3.0); // wait this many seconds, of low feature tracking count before declaring kidnap\n\n\n    ros::Rate loop_rate( loop_rate_hz );\n    int prev_count = 0, new_count = 0;\n    ros::Time last_known_keyframe;\n\n    // TODO: Move both of these to class variables and make them atomic. is_kidnapped_start is valid only when is_kidnapped==true\n    bool is_kidnapped = false;\n    bool is_kidnapped_more_than_n_sec = false;\n    ros::Time is_kidnapped_start;\n\n\n    // Related to handling to playing multiple bags one-after-another.\n    bool first_data_received = false;\n    bool waiting_for_next_bag_to_start = false;\n\n    while( b_kidnaped_thread_enable ) {\n        // Book Keeping\n        prev_count = new_count;\n        loop_rate.sleep();\n\n\n\n        auto data_map = dataManager->getDataMapRef(); //< map< ros::Time, DataNode*>\n        new_count = data_map->size();\n\n        if( new_count <= prev_count ) {\n            __Cerebro__kidnaped_thread__(cout << \"[Cerebro::kidnaped_thread]Nothing new\\n\";)\n            continue;\n        }\n\n        ros::Time lb = data_map->rbegin()->first - ros::Duration(5, 0); // look at recent 5sec.\n        // auto S = data_map.begin();\n        auto S = data_map->lower_bound( lb );\n        auto E = data_map->end();\n        __Cerebro__kidnaped_thread__debug( cout << \"[Cerebro::kidnaped_thread] S=\" << S->first << \"  E=\" << E->first <<  endl; )\n\n        for( auto it = S ; it != E ; it++ )\n        {\n\n            #if 0\n            if( it->second->isImageAvailable() ) {\n                cout << TermColor::GREEN();\n            } else { cout << TermColor::BLUE() ; }\n\n            if( it->second->isPoseAvailable() && it->second->getNumberOfSuccessfullyTrackedFeatures() < 0 ) {\n                cout << \"A\";\n            }\n            if( !it->second->isPoseAvailable() && it->second->getNumberOfSuccessfullyTrackedFeatures() < 0 ) {\n                cout << \"B\";\n            }\n            if( it->second->isPoseAvailable() && ! (it->second->getNumberOfSuccessfullyTrackedFeatures() < 0 ) ) {\n                cout << \"C\";\n            }\n            if( !it->second->isPoseAvailable() && ! (it->second->getNumberOfSuccessfullyTrackedFeatures() < 0 )  ){\n                cout << \"D\";\n            }\n            cout << TermColor::RESET() ;\n            #endif\n\n\n            int n_feats = it->second->getNumberOfSuccessfullyTrackedFeatures();\n            if( it->first <= last_known_keyframe ||  n_feats < 0 )\n                continue;\n\n            __Cerebro__kidnaped_thread__debug(\n                if( n_feats > 50 )\n                    cout <<it->first << \": n_feats=\" << TermColor::iGREEN() << n_feats << TermColor::RESET() << endl;\n                if( n_feats > 30 && n_feats<=50 )\n                    cout <<it->first << \": n_feats=\" << TermColor::GREEN() << n_feats << TermColor::RESET() << endl;\n                if( n_feats > 20 && n_feats<=30 )\n                    cout <<it->first << \": n_feats=\" << TermColor::YELLOW() << n_feats << TermColor::RESET() << endl;\n                if( n_feats > 10 && n_feats<=20 )\n                    cout <<it->first << \": n_feats=\" << TermColor::RED() << n_feats << TermColor::RESET() << endl;\n                if( n_feats<=10 )\n                    cout <<it->first << \": n_feats=\" << TermColor::iRED() << n_feats << TermColor::RESET() << endl;\n            )\n\n            last_known_keyframe = it->first;\n\n\n            if( is_kidnapped==false && n_feats < THRESH_N_FEATS  ) {\n                is_kidnapped = true;\n                is_kidnapped_start = it->first;\n\n                __Cerebro__kidnaped_thread__(\n                cout << TermColor::RED() << \"I am kidnapped t=\" << it->first << TermColor::RESET() << endl;\n                cout << \"I think so because the number of tracked features (from feature tracker) have fallen to only \"\n                     << n_feats << \", the threshold was \" << THRESH_N_FEATS\n                     << \". However, I will wait for \" << WAIT_BEFORE_DECLARING_AS_KIDNAP << \" sec to declare kidnapped to vins_estimator.\" << endl;\n                )\n            }\n\n            __Cerebro__kidnaped_thread__(\n            if( is_kidnapped ) {\n                    cout << \"is_kidnapped=true t=\" << it->first << \"  n_feats=\" << n_feats << endl;\n            }\n            )\n\n            if( is_kidnapped && !is_kidnapped_more_than_n_sec && (it->first - is_kidnapped_start) > WAIT_BEFORE_DECLARING_AS_KIDNAP )\n            {\n                __Cerebro__kidnaped_thread__(\n                cout << \"Kidnapped for more than \" << WAIT_BEFORE_DECLARING_AS_KIDNAP << \"sec. I am quite confident that I have been kidnapped\\n\";\n                cout << \"PUBLISH FALSE\\n\";\n                )\n                is_kidnapped_more_than_n_sec = true;\n\n                dataManager->PUBLISH__FALSE( is_kidnapped_start );\n\n            }\n\n            if( is_kidnapped && n_feats > THRESH_N_FEATS ) {\n                __Cerebro__kidnaped_thread__(\n                cout << TermColor::GREEN() << \"Looks like i have been unkidnapped t=\" << it->first << TermColor::RESET() << endl;\n                )\n\n                if( is_kidnapped_more_than_n_sec )\n                {\n                    // publish true to vins_estimator to indicate that it may resume the estimation with a new co-ordinate system.\n                    dataManager->PUBLISH__TRUE( it->first );\n\n                }\n                is_kidnapped = false;\n                is_kidnapped_more_than_n_sec = false;\n\n            }\n        }\n        // cout << endl;\n\n\n    }\n\n\n    cout << TermColor::RED() << \"Cerebro::kipnaped_thead Ends\\n\" << TermColor::RESET() << endl;\n}\n\n\nbool Cerebro::kidnap_info( int i, ros::Time& start_, ros::Time& end_ )\n{\n    std::lock_guard<std::mutex> lk(mutex_kidnap);\n    if( i>=0 && i<start_of_kidnap.size() ) {\n        start_ = start_of_kidnap[i];\n        end_ = end_of_kidnap[i];\n        return true;\n    }\n    else {\n        start_ = ros::Time();\n        end_ = ros::Time();\n        return false;\n    }\n}\n\njson Cerebro::kidnap_info_as_json()\n{\n    std::lock_guard<std::mutex> lk(mutex_kidnap);\n    json json_obj;\n    json_obj[\"meta_info\"][\"state_is_kidnapped\"] = (bool) state_is_kidnapped;\n    json_obj[\"meta_info\"][\"len_start_of_kidnap\"] = start_of_kidnap.size();\n    json_obj[\"meta_info\"][\"len_end_of_kidnap\"] = end_of_kidnap.size();\n    for( int i=0 ; i<start_of_kidnap.size() ; i++ )\n    {\n        json tmp;\n        tmp[\"start_of_kidnap_sec\"] = start_of_kidnap[i].sec;\n        tmp[\"start_of_kidnap_nsec\"] = start_of_kidnap[i].nsec;\n        tmp[\"end_of_kidnap_sec\"] = end_of_kidnap[i].sec;\n        tmp[\"end_of_kidnap_nsec\"] = end_of_kidnap[i].nsec;\n        json_obj[\"info\"].push_back( tmp );\n    }\n    return json_obj;\n}\n\nint Cerebro::n_kidnaps() //< will with return length of `start_of_kidnap` current state has to be inferred by call to `is_kidnapped()`\n{\n    std::lock_guard<std::mutex> lk(mutex_kidnap);\n    return start_of_kidnap.size();\n}\n\nbool Cerebro::is_kidnapped(){ state_is_kidnapped; }\n\n\n#define __CEREBRO_kidnap_callbacks(msg) msg;\n// #define __CEREBRO_kidnap_callbacks(msg) ;\n\nvoid Cerebro::kidnap_bool_callback( const std_msgs::BoolConstPtr& rcvd_bool )\n{\n    return; // ignore this.\n    __CEREBRO_kidnap_callbacks( cout << TermColor::iCYAN() << \"[Cerebro::kidnap_bool_callback] msg=\" << (bool)rcvd_bool->data << TermColor::RESET() << endl; )\n}\n\nvoid Cerebro::kidnap_header_callback( const std_msgs::HeaderConstPtr& rcvd_header )\n{\n    __CEREBRO_kidnap_callbacks(\n    cout << TermColor::iCYAN() << \"[Cerebro::kidnap_header_callback]\" ;\n    cout << rcvd_header->stamp << \":\" << rcvd_header->frame_id ;\n    cout << TermColor::RESET() << endl;\n    )\n\n    if( rcvd_header->frame_id == \"kidnapped\" ) {\n        std::lock_guard<std::mutex> lk(mutex_kidnap);\n        this->state_is_kidnapped = true;\n        start_of_kidnap.push_back( rcvd_header->stamp );\n        __CEREBRO_kidnap_callbacks(\n        cout << TermColor::iCYAN() << \"start_of_kidnap.push_back(\"<< rcvd_header->stamp << \")\" << TermColor::RESET() << endl;\n        )\n        return;\n    }\n\n    if( rcvd_header->frame_id == \"unkidnapped\" ) {\n        std::lock_guard<std::mutex> lk(mutex_kidnap);\n        this->state_is_kidnapped = false;\n        end_of_kidnap.push_back( rcvd_header->stamp );\n        __CEREBRO_kidnap_callbacks(\n        cout << TermColor::iCYAN() << \"end_of_kidnap.push_back(\"<< rcvd_header->stamp << \")\" << TermColor::RESET() << endl;\n        )\n        return;\n    }\n\n    assert( false && \"in Cerebro::kidnap_header_callback. rcvd_header->frame_id is something other than `kidnapped` or `unkidnapped`.\");\n\n}\n\n\n\n\n\n\n///////////////////////////////////////////////////////////\n//---------------------- KIDNAP -------------------------//\n///////////////////////////////////////////////////////////\n\n\n\n//-----------------------------------------------------------------\n// Cerebro Private Utils\n//-----------------------------------------------------------------\n// private function for descriptor_dot_product thread\nbool Cerebro::wait_until__connectedToDescServer_and_descSizeAvailable( int timeout_in_sec )  //blocking call\n{\n    assert( timeout_in_sec > 2 );\n    ros::Rate rate(10);\n    // wait until connected_to_descriptor_server=true and descriptor_size_available=true\n    int wait_itr = 0;\n    while( true ) {\n        if( this->connected_to_descriptor_server && this->descriptor_size_available)\n            break;\n        __Cerebro__run__(cout << wait_itr << \" [Cerebro::wait_until__connectedToDescServer_and_descSizeAvailable] waiting for `descriptor_size_available` to be true. timeout_in_sec=\" << timeout_in_sec << \"\\n\";)\n        rate.sleep();\n        wait_itr++;\n        if( wait_itr > timeout_in_sec*10 ) {\n            __Cerebro__run__( cout << TermColor::RED() << \"[Cerebro::wait_until__connectedToDescServer_and_descSizeAvailable] `this->connected_to_descriptor_server && this->descriptor_size_available` has not become true dispite waiting for about \"<< timeout_in_sec << \" sec. So quiting the run thread.\\n\" << TermColor::RESET(); )\n            return false;\n        }\n    }\n    return true;\n}\n\n\n//-----------------------------------------------------------------\n// END Cerebro Private Utils\n//-----------------------------------------------------------------\n"
  },
  {
    "path": "src/Cerebro.h",
    "content": "#pragma once\n\n/** Cerebro Class\n        This class is suppose to be the main-brain of this package.\n        It has to run its own threads (should not block)\n\n        It can access DataManager::camera, DataManager::imu_T_cam, DataManager::data_map.\n\n        Author  : Manohar Kuse <mpkuse@connect.ust.hk>\n        Created : 29th Oct, 2018\n*/\n\n#include <ros/ros.h>\n#include <ros/package.h>\n#include <std_msgs/Bool.h>\n#include <iostream>\n#include <vector>\n\n#include \"PinholeCamera.h\"\n#include \"DataManager.h\"\n#include \"ProcessedLoopCandidate.h\"\n#include \"DlsPnpWithRansac.h\"\n#include \"HypothesisManager.h\"\n\n#include \"utils/TermColor.h\"\n#include \"utils/ElapsedTime.h\"\n#include \"utils/Plot2Mat.h\"\n\n#include \"utils/CameraGeometry.h\"\n#include \"utils/PointFeatureMatching.h\"\n\n\n#include \"utils/nlohmann/json.hpp\"\nusing json = nlohmann::json;\n\n// ROS-Service Defination\n#include <cerebro/WholeImageDescriptorCompute.h>\n\n// #include <theia/theia.h>\n\n#include <Eigen/Dense>\n#include <Eigen/Geometry>\nusing namespace Eigen;\nusing namespace std;\n\n\n//comment this out to remove dependence on faiss.\n// If using faiss, also remember to link to libfaiss.so. See my CMakeList file to know how to do it.\n// #define HAVE_FAISS\n\n#ifdef HAVE_FAISS\n// faiss is only used for generating loopcandidates.\n// only function that uses faiss is `Cerebro::faiss__naive_loopcandidate_generator()`\n// Although faiss is strictly not needed, it is a much // faster way for nearest neighbour search. If you don't want it\n// just use the naive implementation: descrip_N__dot__descrip_0_N().\n//    Note: Cerebro::descrip_N__dot__descrip_0_N() and Cerebro::faiss__naive_loopcandidate_generator()\n//    are exactly same in functionality.\n#include <faiss/IndexFlat.h>\n#endif //HAVE_FAISS\n\nclass Cerebro\n{\n\n    //-------------- Constructor --------------------//\npublic:\n    Cerebro( ros::NodeHandle& nh  ); // TODO removal of nh argument.\n    void setDataManager( DataManager* dataManager );\n    void setPublishers( const string base_topic_name );\n\nprivate:\n    // global private variables\n    bool m_dataManager_available=false;\n    DataManager * dataManager;\n    ros::NodeHandle nh; ///< This is here so that I can set new useful publishers\n\n    bool m_pub_available = false;\n    ros::Publisher pub_loopedge;\n\n    //-------------- END Constructor --------------------//\n\n\n\n\n    //--------------- Descriptor Computation Thread ------------------//\npublic:\n    // This monitors the dataManager->data_map and makes sure the descriptor are uptodate.\n    // descriptors are computed by an external ros-service. in the future can have\n    // more similar threads to compute object bounding boxes, text and other perception related services.\n    void descriptor_computer_thread_enable() { b_descriptor_computer_thread = true; }\n    void descriptor_computer_thread_disable() { b_descriptor_computer_thread = false; }\n    void descriptor_computer_thread();\n\nprivate:\n    atomic<bool> b_descriptor_computer_thread;\n    atomic<bool> connected_to_descriptor_server;\n    atomic<bool> descriptor_size_available;\n    atomic<int> descriptor_size;\n\n\n    // Storage for Intelligence\n    mutable std::mutex m_wholeImageComputedList;\n    vector<ros::Time> wholeImageComputedList; ///< A list of stamps where descriptors are computed and available.\n    void wholeImageComputedList_pushback( const ros::Time __tx ); //this is kept private on purpose so that others from outside cannot pushback here.\npublic:\n    const int wholeImageComputedList_size() const; //size of the list. threadsafe\n    const ros::Time wholeImageComputedList_at(int k) const; //< returns kth element of the list. threadsafe\n    //--------------- END Descriptor Computation Thread ------------------//\n\n\n\n\n    //---------------- Populate Loop Candidates --------------------//\npublic:\n    // This is supposed to be run in a separate thread.\n    void run_thread_enable() { b_run_thread = true; }\n    void run_thread_disable() { b_run_thread = false; }\n    void run(); //< The loopcandidate (geometrically unverified) producer.\n\n    //###  Method-A :  Naive method of dot product DIY\n    void descrip_N__dot__descrip_0_N(); //< Naive method of dot product DIY\n\n    #ifdef HAVE_FAISS\n    //###  Method-B :  same functionality to descrip_N__dot__descrip_0_N() but uses facebook's faiss library\n    void faiss__naive_loopcandidate_generator();\n\n\n    //###  Method-C:\n    //###      In this we get say 5 nearest neighbour for every l_i.\n    //###      Collect nearest neigbours for say 4 consecutive l_i's.\n    //###      Nearby predictions are merged.\n    void faiss_clique_loopcandidate_generator();\n\n\n    //### Method-D:\n    //          Uses a separate hypothesis manager. My multihyp framework.\n    //          elaborate scheme, still under development.\n    void faiss_multihypothesis_tracking();\n    #endif //HAVE_FAISS\n\n\n\nprivate:\n    // private things to run thread\n    atomic<bool> b_run_thread;\n    bool wait_until__connectedToDescServer_and_descSizeAvailable( int timeout_in_sec );  //blocking call\n\n\npublic:\n    // These are loop candidates. These calls are thread-safe\n    //  producer: `run()`\n    //  user: `Visualization::publish_loopcandidates`\n    const int foundLoops_count() const ;\n    const std::tuple<ros::Time, ros::Time, double> foundLoops_i( int i) const;\n    json foundLoops_as_JSON();\n\nprivate:\n    mutable std::mutex m_foundLoops;\n    vector< std::tuple<ros::Time, ros::Time, double> > foundLoops; // a list containing loop pairs. this is populated by `run()`\n\n\n    //---------------- END Populate Loop Candidates --------------------//\n\n\n\n    //------------------ Geometry Thread ---------------------------//\n    // calls this->foundLoops_count() and this->foundLoops_i() and uses dataManager\n    // to geometric verify and to compute the poses of loop-pairs.\npublic:\n    void loopcandidate_consumer_enable() { b_loopcandidate_consumer=true; }\n    void loopcandidate_consumer_disable() { b_loopcandidate_consumer=false; }\n    void loopcandiate_consumer_thread();\n\n    const int processedLoops_count() const;\n    const ProcessedLoopCandidate& processedLoops_i( int i ) const;\n    // TODO: have a function which returns a json of the info in processedloopcandi_list.\n\n\n\n\n\nprivate:\n    atomic<bool> b_loopcandidate_consumer;\n\n    // helpers\n\n    // Processed foundLoops_i[ j ] and writes the info in the object `proc_candi`\n    // bool process_loop_candidate_imagepair( int j, ProcessedLoopCandidate& proc_candi );\n\n    // This function processes the jth loopcandidate and fills in the ProcessedLoopCandidate.\n    // The return status means that some poses were computed. It doesn't mean the poses were consistent.\n    // Infact, nothing about consistency is performed here. It just computes relative poses using 3 indipendent way.\n    bool process_loop_candidate_imagepair_consistent_pose_compute( int j, ProcessedLoopCandidate& proc_candi ); //< enhanced version of the above\n\n    bool init_stereogeom(); // expected to be called in loopcandiate_consumer_thread. this sets the variable `stereogeom`\n    bool retrive_stereo_pair( DataNode* node, cv::Mat& left_image, cv::Mat& right_image, bool bgr2gray=true );\n    std::shared_ptr<StereoGeometry> stereogeom;\n\n\n    mutable std::mutex m_processedLoops;\n    vector< ProcessedLoopCandidate > processedloopcandi_list;\n\n\n\n    //------------------ END Geometry Thread ---------------------------//\n\n\n    //--------------- kidnaped identification thread ------------------//\npublic:\n    void kidnaped_thread( int loop_rate_hz=5);\n    void kidnaped_thread_enable() {b_kidnaped_thread_enable=true;};\n    void kidnaped_thread_disable() {b_kidnaped_thread_enable=false;};\n\n    bool is_kidnapped();  // gives the current (kidnap) status. threadsafe\n    bool kidnap_info( int i, ros::Time& start_, ros::Time& end_ ); //< returns true if i was a valid kidnap index. return false if it was an invalid index, ie. such kidnap didnt exist\n    json kidnap_info_as_json();\n    int n_kidnaps(); //< will with return length of `start_of_kidnap` current state has to be inferred by call to `is_kidnapped()`\n\n    // kidnap callbacks\n    void kidnap_bool_callback( const std_msgs::BoolConstPtr& rcvd_header ) ;\n    void kidnap_header_callback( const std_msgs::HeaderConstPtr& rcvd_header ) ;\n\nprivate:\n    atomic<bool> b_kidnaped_thread_enable;\n\n    std::mutex mutex_kidnap;\n    atomic< bool > state_is_kidnapped;\n    vector< ros::Time > start_of_kidnap;\n    vector< ros::Time > end_of_kidnap;\n\n    ros::Publisher rcvd_flag_pub;\n    ros::Publisher kidnap_indicator_header_pub;\n    bool is_kidnapn_indicator_set = false;\n};\n"
  },
  {
    "path": "src/DataManager.cpp",
    "content": "#include \"DataManager.h\"\n\nDataManager::DataManager(ros::NodeHandle &nh )\n//: out_stream(ofstream(\"/dev/pts/0\",ios::out) )\n{\n    this->nh = nh;\n}\n\n\n\nDataManager::DataManager(const DataManager &obj)\n//: out_stream(ofstream(\"/dev/pts/0\",ios::out) )\n{\n   cout << \"Copy constructor allocating ptr.\" << endl;\n}\n\n\n\n//////////////////////////////////////////////////////////////////////////////\n//////////////////// Setters and Getters for global info /////////////////////\n//////////////////////////////////////////////////////////////////////////////\n\n// void DataManager::setCamera( const PinholeCamera& camera )\n// {\n//   this->camera = camera;\n//\n//   cout << \"--- Camera Params from DataManager ---\\n\";\n//   this->camera.printCameraInfo();\n//   // cout << \"K\\n\" << this->camera.e_K << endl;\n//   // cout << \"D\\n\" << this->camera.e_D << endl;\n//   cout << \"--- END\\n\";\n// }\n\n\n\n\nvoid DataManager::setAbstractCamera( camodocal::CameraPtr abs_camera, short cam_id )\n{\n    assert( cam_id >= 0 && \"DataManager, you requested a camera with negative id which is an error. cam_id=0 for default camera and 1,2,.. for additional cameras.\\n\" );\n    assert(abs_camera && \"[DataManager::setCamera] in datamanager you are trying to set an invalid abstract camera. You need to loadFromYAML before you can set this camera\\n\");\n    // this->abstract_camera = abs_camera;\n    this->all_abstract_cameras[cam_id] = abs_camera;\n\n    cout << \"--- Abstract CameraParams(cam_id=\" << cam_id << \") from DataManager ---\\n\";\n    // cout << this->abstract_camera->parametersToString() << endl;\n    cout << this->all_abstract_cameras[cam_id]->parametersToString() << endl;\n    cout << \"--- END\\n\";\n}\n\ncamodocal::CameraPtr DataManager::getAbstractCameraRef(short cam_id) const\n{\n    assert( cam_id >= 0 && \"DataManager, you requested a camera with negative id which is an error. cam_id=0 for default camera and 1,2,.. for additional cameras.\\n\" );\n    // assert( abstract_camera && \"[DataManager::getAbstractCameraRef] you are requesting a camera reference before setting.\\n\" );\n    // return abstract_camera;\n    assert( isAbstractCameraSet(cam_id) && \"[DataManager::getAbstractCameraRef] you are requesting a camera reference before setting.\\n\");\n    // return this->all_abstract_cameras[cam_id]; //old non-const access\n    return this->all_abstract_cameras.at(cam_id);\n}\n\nbool DataManager::isAbstractCameraSet(short cam_id) const\n{\n    assert( cam_id >= 0 && \"DataManager, you requested a camera with negative id which is an error. cam_id=0 for default camera and 1,2,.. for additional cameras.\\n\" );\n    if( this->all_abstract_cameras.count( cam_id ) > 0 ) {\n        return true;\n        // if( this->all_abstract_cameras[cam_id] )\n            // return true;\n        // else\n            // return false;\n    }\n    else {\n        return false;\n    }\n}\n\nvector<short> DataManager::getAbstractCameraKeys() const {\n    vector<short> keys;\n    for( auto it=this->all_abstract_cameras.begin(); it!=all_abstract_cameras.end() ; it++ ) {\n        keys.push_back( it->first );\n    }\n    return keys;\n}\n\n\nvoid DataManager::setCameraRelPose( Matrix4d a_T_b, std::pair<int,int> pair_a_b )\n{\n    // assert a and b abstract cameras exisits\n    assert( isAbstractCameraSet(pair_a_b.first) && isAbstractCameraSet(pair_a_b.second) && \"in [DataManager::setCameraRelPose] one of the abstract-cameras were not set, even though you asked me to set their relative pose. You need to set the cameras first before you can set the relative poses.\\n\" );\n\n    cout << \"---DataManager::setCameraRelPose---\\n\";\n    cout << \"setting \"<< pair_a_b.first << \"_T_\" << pair_a_b.second << \" :::> \" << PoseManipUtils::prettyprintMatrix4d( a_T_b ) << endl;\n\n    cam_relative_poses[ pair_a_b ] = a_T_b;\n    cout << \"---DONE---\\n\";\n\n}\n\nbool DataManager::isCameraRelPoseSet( std::pair<int,int> pair_a_b ) const\n{\n    if( this->cam_relative_poses.count( pair_a_b ) > 0 )\n        return true;\n    else\n        return false;\n}\n\nconst Matrix4d& DataManager::getCameraRelPose( std::pair<int,int> pair_a_b ) const\n{\n    assert( isCameraRelPoseSet( pair_a_b ) && \"[DataManager::getCameraRelPose] make sure the rel cam pose you are requesting is available\\n\" );\n    if( !isCameraRelPoseSet( pair_a_b) ) {\n        ROS_ERROR( \"[DataManager::getCameraRelPose] make sure the rel cam pose you are requesting is available\\nYou requested (%d,%d) which is not available\", pair_a_b.first, pair_a_b.second );\n        exit(2);\n    }\n\n    // return this->cam_relative_poses[ pair_a_b ]; //old non const\n    return this->cam_relative_poses.at( pair_a_b );\n}\n\n\nvector< std::pair<int,int> > DataManager::getCameraRelPoseKeys()\n{\n    vector< pair<int,int> > keys;\n    for( auto it=this->cam_relative_poses.begin(); it!=cam_relative_poses.end() ; it++ ) {\n        keys.push_back( it->first );\n    }\n    return keys;\n}\n\nconst Matrix4d& DataManager::getIMUCamExtrinsic() const\n{\n    std::lock_guard<std::mutex> lk(global_vars_mutex);\n    assert( imu_T_cam_available && \"[DataManager::getIMUCamExtrinsic] you request the value before setting it\\n\");\n    return imu_T_cam;\n}\n\nbool DataManager::isIMUCamExtrinsicAvailable() const\n{\n    std::lock_guard<std::mutex> lk(global_vars_mutex);\n    return imu_T_cam_available;\n}\n\nconst ros::Time DataManager::getIMUCamExtrinsicLastUpdated() const\n{\n    std::lock_guard<std::mutex> lk(global_vars_mutex);\n    return imu_T_cam_stamp;\n}\n\n\n\nvoid DataManager::print_datamap_status( string fname ) const\n{\n    ofstream myfile;\n    myfile.open (fname);\n\n\n    myfile << \"#Nodes=\" << data_map->size();\n    if( data_map->size() > 0 ) {\n    myfile << \"\\tBEGIN=\"<< data_map->begin()->first;\n    myfile << \"\\tEND=\"<< data_map->rbegin()->first;\n    }\n    myfile << endl;\n\n    // TODO other global info like imu_T_cam. etc.\n    for( auto it = data_map->begin() ; it!= data_map->end() ; it++ )\n    {\n        string s = \"|\";\n        s+= (it->second->isKeyFrame())?TermColor::iGREEN():\"\";\n        s+= (it->second->isPoseAvailable())?\"P\":\"~\";\n        s+= (it->second->isWholeImageDescriptorAvailable())?\"D\":\"~\";\n\n        myfile << s << TermColor::RESET();\n    }\n    myfile << endl;\n\n    myfile.close();\n}\n\n//////////////////////////////////////////////////////////////////////////////\n/////////////////// Kidnap Indicator Publisher ///////////////////////////////\n//////////////////////////////////////////////////////////////////////////////\nbool DataManager::isKidnapIndicatorPubSet() const\n{\n    return (bool)is_kidnapn_indicator_set ;\n}\n\nvoid DataManager::setKidnapIndicatorPublishers( ros::Publisher& pub_bool, ros::Publisher& pub_header )\n{\n    rcvd_flag_pub = pub_bool;\n    kidnap_indicator_header_pub = pub_header;\n    is_kidnapn_indicator_set = true;\n}\n\n\nvoid DataManager::PUBLISH__TRUE( const ros::Time _t ) const\n{\n    assert( is_kidnapn_indicator_set );\n    cout << TermColor::RED() << \"[Cerebro::PUBLISH__TRUE] PUBLISH TRUE (t= \" << _t << \" to indicate the vins_estimator to start again.\\n\" << TermColor::RESET();\n\n    std_msgs::Bool bool_msg; bool_msg.data = true;\n    rcvd_flag_pub.publish( bool_msg );\n\n    // publish header msg\n    std_msgs::Header header_msg;\n    header_msg.stamp =  _t;\n    header_msg.frame_id = \"unkidnapped\";\n    kidnap_indicator_header_pub.publish( header_msg );\n}\n\nvoid DataManager::PUBLISH__FALSE( const ros::Time _t ) const\n{\n    assert( is_kidnapn_indicator_set );\n    cout << TermColor::RED() << \"[Cerebro::PUBLISH__FALSE] PUBLISH FALSE (t= \" << _t << \" to indicate the vins_estimator to stop.\\n\" << TermColor::RESET();\n\n    // publish False (bool msg)\n    std_msgs::Bool bool_msg; bool_msg.data = false;\n    rcvd_flag_pub.publish( bool_msg );\n\n    // publish header message\n    std_msgs::Header header_msg;\n    header_msg.stamp = _t;\n    header_msg.frame_id = \"kidnapped\";\n    kidnap_indicator_header_pub.publish( header_msg );\n}\n\n\n//////////////////////////////////////////////////////////////////////////////\n//////////////////////////////// call backs //////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////\n\n// #define __DATAMANAGER_CALLBACK_PRINT( u ) u;\n#define __DATAMANAGER_CALLBACK_PRINT( u ) ;\n\nvoid DataManager::camera_pose_callback( const nav_msgs::Odometry::ConstPtr msg )\n{\n    if( pose_0_available == false ) { //record the 1st pose\n        pose_0 = msg->header.stamp;\n        pose_0_available = true;\n    }\n\n    // __DATAMANAGER_CALLBACK_PRINT( cout << TermColor::BLUE()  <<  \"[cerebro/camera_pose_callback del]\" <<  msg->header.stamp-pose_0 << TermColor::RESET() << endl; )\n    __DATAMANAGER_CALLBACK_PRINT( cout << TermColor::BLUE()  <<  \"[cerebro/camera_pose_callback]\" <<  msg->header.stamp << \"\\t\" << msg->header.stamp-pose_0 << TermColor::RESET() << endl; )\n    // push this to queue. Another thread will associate the data\n    pose_buf.push( msg );\n    return;\n\n\n}\n\n// currently not in use.\nvoid DataManager::keyframe_pose_callback( const nav_msgs::Odometry::ConstPtr msg )\n{\n    //__DATAMANAGER_CALLBACK_PRINT( cout << \"[cerebro/camera_pose_callback del]\" << msg->header.stamp - pose_0 << endl; )\n    __DATAMANAGER_CALLBACK_PRINT( cout << TermColor::iBLUE() << \"[cerebro/keyframe_pose_callback]\" << msg->header.stamp << \"\\t\" << msg->header.stamp-pose_0 << TermColor::RESET() << endl; )\n    // push this to queue. Another thread will associate the data\n    kf_pose_buf.push( msg );\n}\n\n\nvoid DataManager::raw_image_callback( const sensor_msgs::ImageConstPtr& msg )\n{\n    // __DATAMANAGER_CALLBACK_PRINT( cout << TermColor::GREEN() << \"[cerebro/raw_image_callback del]\" << msg->header.stamp-pose_0 << TermColor::RESET() << endl; )\n    __DATAMANAGER_CALLBACK_PRINT( cout << TermColor::GREEN() << \"[cerebro/raw_image_callback]\" << msg->header.stamp << \"\\t\" << msg->header.stamp-pose_0 << TermColor::RESET() << endl; )\n\n\n    if( this->last_image_time != ros::Time() &&\n            (msg->header.stamp.toSec() - this->last_image_time.toSec() > 1.0\n                ||\n            msg->header.stamp.toSec() < this->last_image_time.toSec())\n    )\n    {\n        cout << TermColor::iBLUE()\n        << \"+++++++++++++[DataManager::raw_image_callback] \"\n        << \" curr_image.stamp - prev_image.stamp > 1.0.\\n\"\n        << \"This means an unstable stream or more usually means a new bag has started with --skip-empty in rosbagplay.\\n\"\n        << \"I will publish a FALSE t=\" << 10000 << \" then wait for 500ms and publish TRUE t=\" << 10000\n        << \" TODO Complete this implementation and verify correctness.\"\n        << TermColor::RESET() << endl;\n\n\n\n        cout << TermColor::BLUE() << \"PUBLISH__FALSE\\n\" << TermColor::RESET();\n        PUBLISH__FALSE( this->last_image_time );\n\n        cout << TermColor::BLUE() << \"Sleep 500ms\\n\" << TermColor::RESET();\n        // sleep( 500 ms );\n        std::this_thread::sleep_for (std::chrono::milliseconds(500));\n\n        cout << TermColor::BLUE() << \"PUBLISH__TRUE\\n\" << TermColor::RESET();\n        PUBLISH__TRUE( msg->header.stamp );\n\n\n\n    }\n\n\n    img_buf.push( msg );\n    this->last_image_time = msg->header.stamp;\n    return;\n\n}\n\nvoid DataManager::raw_image_callback_1( const sensor_msgs::ImageConstPtr& msg )\n{\n    // __DATAMANAGER_CALLBACK_PRINT( cout << \"[cerebro/raw_image_callback_1 del]\" << msg->header.stamp-pose_0 << endl; )\n    __DATAMANAGER_CALLBACK_PRINT( cout << \"[cerebro/raw_image_callback_1]\" << msg->header.stamp << \"\\t\" << msg->header.stamp-pose_0 << endl; )\n    img_1_buf.push( msg );\n    return;\n}\n\n\nvoid DataManager::depth_image_callback( const sensor_msgs::ImageConstPtr& msg )\n{\n    __DATAMANAGER_CALLBACK_PRINT(\n    cout << \"[cerebro/depth_image_callback]\" << msg->header.stamp << \"\\t\" << msg->header.stamp-pose_0 << \"\\t\";\n    cout << \"depth image encoding: \" << msg->encoding << endl;\n    )\n    depth_im_buf.push( msg );\n    return ;\n}\n\n\nvoid DataManager::extrinsic_cam_imu_callback( const nav_msgs::Odometry::ConstPtr msg )\n{\n    // __DATAMANAGER_CALLBACK_PRINT( cout << \"[cerebro/extrinsic_cam_imu_callback del]\" << msg->header.stamp-pose_0 << endl; )\n    __DATAMANAGER_CALLBACK_PRINT( cout << \"[cerebro/extrinsic_cam_imu_callback]\" << msg->header.stamp << \"\\t\" << msg->header.stamp-pose_0 << endl; )\n    extrinsic_cam_imu_buf.push( msg );\n}\n\n\n\n// there will be 5 channels. ch[0]: un, ch[1]: vn,  ch[2]: u, ch[3]: v.  ch[4]: globalid of the feature.\n // cout << \"\\tpoints.size() : \"<< msg->points.size(); // this will be N (say 92)\n // cout << \"\\tchannels.size() : \"<< msg->channels.size(); //this will be N (say 92)\n // cout << \"\\tchannels[0].size() : \"<< msg->channels[0].values.size(); //this will be 5.\n // cout << \"\\n\";\n // An Example Keypoint msg\n     // ---\n     // header:\n     //   seq: 40\n     //   stamp:\n     //     secs: 1523613562\n     //     nsecs: 530859947\n     //   frame_id: world\n     // points:\n     //   -\n     //     x: -7.59081602097\n     //     y: 7.11367511749\n     //     z: 2.85602664948\n     //   .\n     //   .\n     //   .\n     //   -\n     //     x: -2.64935922623\n     //     y: 0.853760659695\n     //     z: 0.796766400337\n     // channels:\n     //   -\n     //     name: ''\n     //     values: [-0.06108921766281128, 0.02294199913740158, 310.8721618652344, 260.105712890625, 2.0]\n     //     .\n     //     .\n     //     .\n     //   -\n     //     name: ''\n     //     values: [-0.47983112931251526, 0.8081198334693909, 218.95481872558594, 435.47357177734375, 654.0]\n     //   -\n     //     name: ''\n     //     values: [0.07728647440671921, 1.0073764324188232, 344.2176208496094, 473.7791442871094, 660.0]\n     //   -\n     //     name: ''\n     //     values: [-0.6801641583442688, 0.10506453365087509, 159.75746154785156, 279.6077575683594, 663.0]\nvoid DataManager::ptcld_callback( const sensor_msgs::PointCloud::ConstPtr msg )\n{\n    // __DATAMANAGER_CALLBACK_PRINT( cout << \"[cerebro/ptcld_callback]\" << msg->header.stamp << endl; )\n    __DATAMANAGER_CALLBACK_PRINT( cout << TermColor::YELLOW() << \"[cerebro/ptcld_callback]\" << msg->header.stamp  << \"\\t\" << msg->header.stamp-pose_0 << TermColor::RESET() << endl; )\n    ptcld_buf.push( msg );\n    return;\n\n}\n\n\n// currently not in use\nvoid DataManager::tracked_feat_callback( const sensor_msgs::PointCloud::ConstPtr msg )\n{\n    __DATAMANAGER_CALLBACK_PRINT( cout << \"[cerebro/tracked_feat_callback]\" << msg->header.stamp << \"\\t\" << msg->header.stamp-pose_0 << endl; )\n    trackedfeat_buf.push( msg );\n}\n\n\n\n// #define __json_debuggin(msg) msg;\n#define __json_debuggin(msg) ;\njson DataManager::asJson()\n{\n    json obj;\n    auto __data_map = this->getDataMapRef();\n    IOFormat CSVFormat(StreamPrecision, DontAlignCols, \", \", \",\\t\\n\");\n\n    __json_debuggin( cout << \"[DataManager::asJson] Start loop through data_map\\n\"; )\n\n    #if 1\n    // int max_dist = std::distance( __data_map.begin() , __data_map.end() ); //berks__old\n    int max_dist = std::distance( __data_map->begin() , __data_map->end() );\n\n    // for( auto it = __data_map.begin() ; it!= __data_map.end() ; it++ ) //berks__old\n    for( auto it = __data_map->begin() ; it!= __data_map->end() ; it++ )\n    {\n        // int seq_id = std::distance( __data_map.begin() , it ); // berks__old\n        int seq_id = std::distance( __data_map->begin() , it );\n        __json_debuggin(\n        cout << \"[DataManager::asJson] \" <<  seq_id << \" max dist=\" << max_dist << endl;)\n\n        json curr_obj;\n        curr_obj[\"stamp\"] = it->first.toSec();\n        curr_obj[\"stamp_relative\"] =  ( it->first -  this->getPose0Stamp() ).toSec() ;\n        curr_obj[\"seq\"] = seq_id;\n        __json_debuggin( cout << \"A\\n\"; )\n\n        DataNode * __u = it->second;\n        curr_obj[\"getT\"] = __u->getT().toSec();\n        #if 0\n        curr_obj[\"getT_image\"] = __u->getT_image().toSec();\n        #endif\n        // curr_obj[\"getT_image_1\"] = __u->getT_image(1).toSec();\n        curr_obj[\"getT_pose\"] = __u->getT_pose().toSec();\n        curr_obj[\"getT_uv\"] = __u->getT_uv().toSec();\n        __json_debuggin( cout << \"B\\n\"; )\n\n        curr_obj[\"isKeyFrame\"] = __u->isKeyFrame();\n        #if 0\n        curr_obj[\"isImageAvailable\"] = __u->isImageAvailable();\n        curr_obj[\"isImageAvailable_1\"] = __u->isImageAvailable(1);\n        #endif\n        curr_obj[\"isPoseAvailable\"] = __u->isPoseAvailable();\n        curr_obj[\"isPtCldAvailable\"] = __u->isPtCldAvailable();\n        curr_obj[\"isUnVnAvailable\"] = __u->isUnVnAvailable();\n        curr_obj[\"isUVAvailable\"] = __u->isUVAvailable();\n        curr_obj[\"isFeatIdsAvailable\"] = __u->isFeatIdsAvailable();\n        curr_obj[\"getNumberOfSuccessfullyTrackedFeatures\"] = __u->getNumberOfSuccessfullyTrackedFeatures();\n        curr_obj[\"isWholeImageDescriptorAvailable\"] = __u->isWholeImageDescriptorAvailable();\n        __json_debuggin( cout << \"C\\n\"; )\n\n        if( __u->isPoseAvailable() )\n        {\n            const Matrix4d wTc = __u->getPose();\n            json pose_ifo;\n            pose_ifo[\"rows\"] = wTc.rows();\n            pose_ifo[\"cols\"] = wTc.cols();\n            pose_ifo[\"stamp\"] = __u->getT_pose().toSec();\n            std::stringstream ss;\n            ss <<  wTc.format(CSVFormat);\n            pose_ifo[\"data\"] = ss.str();\n            pose_ifo[\"data_pretty\"] = PoseManipUtils::prettyprintMatrix4d(wTc);\n            curr_obj[\"w_T_c\"] = pose_ifo;\n        }\n        __json_debuggin( cout << \"D\\n\"; )\n\n\n\n        obj[\"DataNodes\"].push_back( curr_obj );\n    }\n    #endif\n\n    __json_debuggin( cout << \"[DataManager::asJson] End loop through data_map\\n\"; )\n\n\n    // Global Data\n    __json_debuggin( cout << \"[DataManager::asJson] Logging Global data\\n\"; )\n    obj[\"global\"][\"isPose0Available\"] = this->isPose0Available();\n    obj[\"global\"][\"Pose0Stamp\"] = this->getPose0Stamp().toSec();\n\n    obj[\"global\"][\"isIMUCamExtrinsicAvailable\"] = this->isIMUCamExtrinsicAvailable();\n    if( this->isIMUCamExtrinsicAvailable() )\n    {\n        const Matrix4d __imu_T_cam = this->getIMUCamExtrinsic();\n        json pose_ifo;\n        pose_ifo[\"rows\"] = __imu_T_cam.rows();\n        pose_ifo[\"cols\"] = __imu_T_cam.cols();\n        pose_ifo[\"stamp\"] = this->getIMUCamExtrinsicLastUpdated().toSec();\n        std::stringstream ss;\n        ss <<  __imu_T_cam.format(CSVFormat);\n        pose_ifo[\"data\"] = ss.str();\n        pose_ifo[\"data_pretty\"] = PoseManipUtils::prettyprintMatrix4d(__imu_T_cam);\n        obj[\"global\"][\"Pose0Stamp\"] = pose_ifo;\n    }\n\n\n    __json_debuggin( cout << \"[DataManager::asJson] Done asJson()\\n\"; )\n    return obj;\n\n}\n\n\n\nstring DataManager::print_queue_size( int verbose=1 ) const\n{\n    std::stringstream buffer;\n\n    if( verbose == 1)\n    {\n        buffer << \"img_buf=\" << img_buf.size() << \"\\t\";\n        buffer << \"img_1_buf=\" << img_1_buf.size() << \"\\t\";\n        buffer << \"depth_im_buf=\" << depth_im_buf.size() << \"\\t\";\n        buffer << \"pose_buf=\" << pose_buf.size() << \"\\t\";\n        buffer << \"kf_pose_buf=\" << kf_pose_buf.size() << \"\\t\";\n        buffer << \"ptcld_buf=\" << ptcld_buf.size() << \"\\t\";\n        buffer << \"trackedfeat_buf=\" << trackedfeat_buf.size() << \"\\t\";\n        buffer << \"extrinsic_cam_imu_buf=\" << extrinsic_cam_imu_buf.size() << \"\\t\";\n        buffer << endl;\n    }\n\n    if( verbose == 2 )\n    {\n        buffer << \"img_buf=\" << img_buf.size() << \" (\";\n        if( img_buf.size()  > 0 ) {\n            buffer << std::fixed << std::setprecision(4) << img_buf.front()->header.stamp-pose_0 << \"-->\";\n            buffer << std::fixed << std::setprecision(4) << img_buf.back()->header.stamp-pose_0 << \";\";\n        }\n        buffer << \")\\t\";\n\n        buffer << \"img_1_buf=\" << img_1_buf.size() << \" (\";\n        if( img_1_buf.size()  > 0 ) {\n            buffer << std::fixed << std::setprecision(4) << img_1_buf.front()->header.stamp-pose_0 << \"-->\";\n            buffer << std::fixed << std::setprecision(4) << img_1_buf.back()->header.stamp-pose_0 << \";\";\n        }\n        buffer << \")\\t\";\n\n        buffer << \"depth_im_buf=\" << depth_im_buf.size() << \" (\";\n        if( depth_im_buf.size()  > 0 ) {\n            buffer << std::fixed << std::setprecision(4) << depth_im_buf.front()->header.stamp-pose_0 << \"-->\";\n            buffer << std::fixed << std::setprecision(4) << depth_im_buf.back()->header.stamp-pose_0 << \";\";\n        }\n        buffer << \")\\t\";\n\n        buffer << \"pose_buf=\" << pose_buf.size() << \" (\";\n        if( pose_buf.size()  > 0 ) {\n            buffer << std::fixed << std::setprecision(4) << pose_buf.front()->header.stamp-pose_0 << \"-->\";\n            buffer << std::fixed << std::setprecision(4) << pose_buf.back()->header.stamp-pose_0 << \";\";\n        }\n        buffer << \")\\t\";\n\n        buffer << \"kf_pose_buf=\" << kf_pose_buf.size() << \" (\";\n        if( kf_pose_buf.size()  > 0 ) {\n            buffer << std::fixed << std::setprecision(4) << kf_pose_buf.front()->header.stamp-pose_0 << \"-->\";\n            buffer << std::fixed << std::setprecision(4) << kf_pose_buf.back()->header.stamp-pose_0 << \";\";\n        }\n        buffer << \")\\t\";\n\n        buffer << \"ptcld_buf=\" << ptcld_buf.size() << \" (\";\n        if( ptcld_buf.size()  > 0 ) {\n            buffer << std::fixed << std::setprecision(4) << ptcld_buf.front()->header.stamp-pose_0 << \"-->\";\n            buffer << std::fixed << std::setprecision(4) << ptcld_buf.back()->header.stamp-pose_0 << \";\";\n        }\n        buffer << \")\\t\";\n\n        buffer << \"trackedfeat_buf=\" << trackedfeat_buf.size() << \" (\";\n        if( trackedfeat_buf.size()  > 0 ) {\n            buffer << std::fixed << std::setprecision(4) << trackedfeat_buf.front()->header.stamp-pose_0 << \"-->\";\n            buffer << std::fixed << std::setprecision(4) << trackedfeat_buf.back()->header.stamp-pose_0 << \";\";\n        }\n        buffer << \")\\t\";\n\n        buffer << \"extrinsic_cam_imu_buf=\" << extrinsic_cam_imu_buf.size() << \" (\";\n        if( extrinsic_cam_imu_buf.size()  > 0 ) {\n            buffer << std::fixed << std::setprecision(4) << extrinsic_cam_imu_buf.front()->header.stamp-pose_0 << \"-->\";\n            buffer << std::fixed << std::setprecision(4) << extrinsic_cam_imu_buf.back()->header.stamp-pose_0 << \";\";\n        }\n        buffer << \")\\t\";\n        buffer << \"\\n\";\n    }\n\n    if( verbose == 3 )\n    {\n        buffer << \"img_buf.len=\" << img_buf.size() << \"\\t\";\n        buffer << \"pose_buf.len=\" << pose_buf.size() << \"\\t\";\n        buffer << \"kf_pose_buf.len=\" << kf_pose_buf.size() << \"\\t\";\n        buffer << \"ptcld_buf.len=\" << ptcld_buf.size() << \"\\t\";\n        buffer << \"trackedfeat_buf.len=\" << trackedfeat_buf.size() << \"\\t\";\n        buffer << \"extrinsic_cam_imu_buf.len=\" << extrinsic_cam_imu_buf.size() << \"\\t\";\n        buffer << endl;\n\n        if( img_buf.size() > 0 )\n            buffer << \"img_buf.back.t=\" << img_buf.back()->header.stamp-pose_0 << \"\\t\";\n        if( pose_buf.size() > 0 )\n            buffer << \"pose_buf.back.t=\" << pose_buf.back()->header.stamp-pose_0 << \"\\t\";\n        if( kf_pose_buf.size() > 0 )\n            buffer << \"kf_pose_buf.back.t=\" << kf_pose_buf.back()->header.stamp-pose_0 << \"\\t\";\n        if( ptcld_buf.size() > 0 )\n            buffer << \"ptcld_buf.back.t=\" << ptcld_buf.back()->header.stamp-pose_0 << \"\\t\";\n        if( trackedfeat_buf.size() > 0 )\n            buffer << \"trackedfeat_buf.back.t=\" << trackedfeat_buf.back()->header.stamp-pose_0 << \"\\t\";\n        if( extrinsic_cam_imu_buf.size() > 0 )\n            buffer << \"extrinsic_cam_imu_buf.back.t=\" << extrinsic_cam_imu_buf.back()->header.stamp-pose_0 << \"\\t\";\n        buffer << endl;\n\n        if( img_buf.size() > 0 )\n            buffer << \"img_buf.front.t=\" << img_buf.front()->header.stamp-pose_0 << \"\\t\";\n        if( pose_buf.size() > 0 )\n            buffer << \"pose_buf.front.t=\" << pose_buf.front()->header.stamp-pose_0 << \"\\t\";\n        if( kf_pose_buf.size() > 0 )\n            buffer << \"kf_pose_buf.front.t=\" << kf_pose_buf.front()->header.stamp-pose_0 << \"\\t\";\n        if( ptcld_buf.size() > 0 )\n            buffer << \"ptcld_buf.front.t=\" << ptcld_buf.front()->header.stamp-pose_0 << \"\\t\";\n        if( trackedfeat_buf.size() > 0 )\n            buffer << \"trackedfeat_buf.front.t=\" << trackedfeat_buf.front()->header.stamp-pose_0 << \"\\t\";\n        if( extrinsic_cam_imu_buf.size() > 0 )\n            buffer << \"extrinsic_cam_imu_buf.front.t=\" << extrinsic_cam_imu_buf.front()->header.stamp-pose_0 << \"\\t\";\n        buffer << endl;\n    }\n\n    return buffer.str();\n}\n\n\n//////////////////////////// Callback ends /////////////////////////////////////\n\n\n\n\n//////////////////////////////////////////////////////////////////////////////\n/////////////////////////// Thread mains /////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////////\n#define _XOUT_ myfile\nvoid DataManager::trial_thread( )\n{\n    cout << TermColor::GREEN() << \"Start DataManager::trial_thread \"<< TermColor::RESET() << endl;\n    ros::Rate looprate(10);\n\n\n    #if 0\n    ofstream myfile;\n    myfile.open (fname);\n    cout << \"[DataManager::trial_thread] Dump output to file: \" << fname << endl;\n\n    while( b_trial_thread )\n    {\n        _XOUT_ << \"trial_thread\\n\";\n\n        // berks__old\n        // auto S = data_map.begin(); //.lower_bound( lb );\n        // auto E = data_map.end();\n\n        auto S = data_map->begin();\n        auto E = data_map->end();\n        _XOUT_ << \"S=\" << S->first << \"  E=\" << E->first <<  endl;\n        int n_green = 0, n_cyan = 0 , n_blue = 0;\n        for( auto it = S ; it != E ; it++ )\n        {\n            #if 1\n            if( it->second->isImageAvailable() && it->second->isImageAvailable(1) ) {\n                _XOUT_ << TermColor::GREEN();n_green++;\n            } else {\n                if( it->second->isImageAvailable() ) {\n                    _XOUT_ << TermColor::CYAN() ;n_cyan++;\n                }\n                else {\n                    _XOUT_ << TermColor::BLUE() ;n_blue++;\n                }\n            }\n\n            if( it->second->isPoseAvailable() && it->second->getNumberOfSuccessfullyTrackedFeatures() < 0 ) {\n                _XOUT_ << \"A\" ;\n                // cout << it->second->getNumberOfSuccessfullyTrackedFeatures() << \",\";\n            }\n            if( !it->second->isPoseAvailable() && it->second->getNumberOfSuccessfullyTrackedFeatures() < 0 ) {\n                _XOUT_ << \"B\" ;\n                // cout << it->second->getNumberOfSuccessfullyTrackedFeatures() << \",\";  // this means there thise node has no pose and no tracked features data\n            }\n            if( it->second->isPoseAvailable() && ! (it->second->getNumberOfSuccessfullyTrackedFeatures() < 0 ) ) {\n                _XOUT_ << \"C\" ;\n                // cout << it->second->getNumberOfSuccessfullyTrackedFeatures() << \",\";\n            }\n            if( !it->second->isPoseAvailable() && ! (it->second->getNumberOfSuccessfullyTrackedFeatures() < 0 )  ){\n                _XOUT_ << \"D\"; // has no pose data but has tracked features. Pose actually takes some time to arrive. tracked features will be avaiaable first,\n            }\n            _XOUT_ << TermColor::RESET() ;\n            #endif\n        }\n        _XOUT_ << endl;\n        _XOUT_ << \"n_green=\" << n_green << \"  \";\n        _XOUT_ << \"n_cyan=\" << n_cyan << \"  \";\n        _XOUT_ << \"n_blue=\" << n_blue << \"  \";\n        _XOUT_ << endl;\n\n\n\n        looprate.sleep();\n    }\n    myfile.close();\n    #endif\n\n\n    while( b_trial_thread )\n    {\n        img_data_mgr->print_status( \"/dev/pts/23\");\n        this->print_datamap_status( \"/dev/pts/24\" );\n        looprate.sleep();\n    }\n\n    cout << TermColor::RED() << \"END DataManager::trial_thread \"<< TermColor::RESET() << endl;\n}\n\n\n// #define ___clean_up_cout(msg) msg;\n#define ___clean_up_cout(msg) ;\nvoid DataManager::clean_up_useless_images_thread()\n{\n    //---\n    // Settings\n    //---\n    int keep_last_n_sec_in_ram = 5; //to be safe keep 10. 3-5 usually is fine.\n\n    cout << TermColor::GREEN() << \"[DataManager::clean_up_useless_images_thread] Start thread \"<< TermColor::RESET() << endl;\n    ros::Rate looprate(0.3);\n    // data_association_thread_disable();\n    while( b_clean_up_useless_images_thread )\n    {\n        looprate.sleep();\n\n        // if( data_map.begin() == data_map.end() ) { //berks__old\n        if( data_map->begin() == data_map->end() ) {\n            cout << TermColor::CYAN() << \"[clean_up_useless_images_thread] no nodes\" << TermColor::RESET() << endl;\n            continue;\n        }\n\n        // berks__old\n        // auto S = data_map.begin();\n        // auto E = data_map.upper_bound( data_map.rbegin()->first - ros::Duration( 3.0 ) );\n\n        auto S = data_map->upper_bound( data_map->rbegin()->first - ros::Duration( keep_last_n_sec_in_ram+5 ) );\n        // auto S = data_map->begin();\n        auto E = data_map->upper_bound( data_map->rbegin()->first - ros::Duration( keep_last_n_sec_in_ram ) );\n        int q=0;\n        ___clean_up_cout( cout << \"[DataManager::clean_up_useless_images_thread] \"  <<  S->first << \" to \" << E->first << \"\\t\"; )\n        ___clean_up_cout( cout << S->first-getPose0Stamp() << \" to \" << E->first - getPose0Stamp() << endl; )\n        // for( auto it = data_map.begin() ; it->first < E->first ; it++ ) { //berks__old\n        for( auto it = S ; it->first < E->first ; it++ ) {\n            #if 0 //old, where images where stored inside datanode.\n            if( it->second->isImageAvailable() && !it->second->isKeyFrame() ) {\n            // if( it->second->isImageAvailable()  ) {\n                ___clean_up_cout(\n                    cout << TermColor::CYAN() << \"deallocate_all_images with t=\" << it->first << \" \" << q++ << TermColor::RESET() << endl;\n                    it->second->print_image_cvinfo();\n                )\n                it->second->deallocate_all_images();\n            }\n            else {\n                ___clean_up_cout( cout << TermColor::YELLOW() << \"not deallocate coz t=\" << it->first << \" aka t=\" << it->first - getPose0Stamp() << \"is a keyframe (has pose info)\" << q++ << TermColor::RESET() << endl; )\n            }\n            #endif\n\n            if( it->second->isKeyFrame() ) {\n                img_data_mgr->stashImage( \"left_image\", it->first );\n                img_data_mgr->stashImage( \"right_image\", it->first );\n                img_data_mgr->stashImage( \"depth_image\", it->first );\n            } else {\n                img_data_mgr->rmImage( \"left_image\", it->first );\n                img_data_mgr->rmImage( \"right_image\", it->first );\n                img_data_mgr->rmImage( \"depth_image\", it->first );\n            }\n        }\n    }\n    cout << TermColor::RED() << \"[DataManager::clean_up_useless_images_thread] Finished Thread\"<< TermColor::RESET() << endl;\n\n}\n\n\n// #define __DataManager__data_association_thread__( msg ) msg;\n#define __DataManager__data_association_thread__( msg ) ;\n\nvoid DataManager::data_association_thread( int max_loop_rate_in_hz )\n{\n    assert( max_loop_rate_in_hz > 0 && max_loop_rate_in_hz < 200 && \"[DataManager::data_association_thread] I am expecting the loop rate to be between 1-50\" );\n    cout << TermColor::GREEN() << \"[DataManager::data_association_thread] Start thread\" << TermColor::RESET() << endl;\n    assert( b_data_association_thread && \"You have not enabled thread execution. Call function DataManager::data_association_thread_enable() before you spawn this thread\\n\");\n    float requested_loop_time_ms = 1000. / max_loop_rate_in_hz;\n\n    while( b_data_association_thread )\n    {\n        auto loop_start_at = std::chrono::high_resolution_clock::now();\n\n        //------------------------ Process here--------------------------------\n        // std::this_thread::sleep_for( std::chrono::milliseconds(2000) );\n        __DataManager__data_association_thread__(\n        cout << \"---\\n\" ;\n        cout << print_queue_size(2 ) ; //< sizes of buffer queues\n        cout << \"\\t\\tSize_of_data_map = \"+ to_string( data_map->size() ) + \"\\n\" ;\n        )\n\n\n        // deqeue all raw images and make DataNodes of each of them, s\n        while( img_buf.size() > 0 ) {\n            sensor_msgs::ImageConstPtr img_msg = img_buf.front();\n            img_buf.pop();\n            __DataManager__data_association_thread__(\n                cout << TermColor::GREEN() << \">>>>>>>>> Added a new DataNode in data_map with poped() rawimage t=\" << img_msg->header.stamp << \" #####> ie.\" << img_msg->header.stamp - pose_0 << TermColor::RESET() << endl;\n            )\n            DataNode * n = new DataNode( img_msg->header.stamp );\n            #if 0\n            n->setImageFromMsg( img_msg );\n            #else\n            img_data_mgr->setNewImageFromMsg( \"left_image\", img_msg );\n            #endif\n\n            // data_map.insert( std::make_pair(img_msg->header.stamp, n) ); //berks__old\n            data_map->insert( std::make_pair(img_msg->header.stamp, n) );\n        }\n\n        // dequeue additional raw images\n        while( img_1_buf.size() > 0 ) {\n            sensor_msgs::ImageConstPtr img_1_msg = img_1_buf.front();\n            ros::Time t = img_1_msg->header.stamp;\n\n            __DataManager__data_association_thread__(\n            cout << \">> Attempt adding poped() img_1_msg in data_map with t=\" << img_1_msg->header.stamp << \" ie. #####> \" << img_1_msg->header.stamp-pose_0 << endl;\n            )\n            // if( data_map.count(t) > 0 ) { //berks__old\n            //     data_map.at( t )->setImageFromMsg( img_1_msg, 1 );\n            // }\n            if( data_map->count(t) > 0 ) {\n                #if 0\n                data_map->at( t )->setImageFromMsg( img_1_msg, 1 );\n                #else\n                img_data_mgr->setNewImageFromMsg( \"right_image\", img_1_msg );\n                #endif\n\n                img_1_buf.pop();\n            }\n            else {\n                ros::Time newest_T = data_map->rbegin()->first;\n                __DataManager__data_association_thread__( cout << \"newest_T=\" << newest_T << endl; ) ;\n                if( newest_T.toNSec() > t.toNSec() )\n                {\n                    cout << \"newest_T is head of the t, so just drop this image\";\n                    img_1_buf.pop();\n                } else {\n                // assert( false && \"[DataManager::data_association_thread] attempting to set additional image into datanode. However that datanode is not found in the map. This cannot be happening\\n\");\n                __DataManager__data_association_thread__(\n                    cout << TermColor::RED() << \"[DataManager::data_association_thread]\";\n                    cout << \"attempting to set additional image_1 with t=\" << t << \" aka \" << t-pose_0 <<  \" into datanode. \";\n                    cout << \"However that datanode is not found in the map. This cannot be happening, but might happen when `image_1` preceeds (even a few milis) than `image`.\";\n                    cout << TermColor::RESET() << endl ;\n\n                cout << TermColor::CYAN() << \"so dont pop from queue.\\n\" << TermColor::RESET();\n                )\n                break;\n                }\n            }\n        }\n\n\n        // depth image\n        while( depth_im_buf.size() > 0 ) {\n            sensor_msgs::ImageConstPtr depth_image_msg = depth_im_buf.front();\n            ros::Time t = depth_image_msg->header.stamp;\n\n            __DataManager__data_association_thread__(\n            cout << \">> Attempt adding poped() depth_image_msg in data_map with t=\" << depth_image_msg->header.stamp << \" ie. #####> \" << depth_image_msg->header.stamp-pose_0 << endl;\n            )\n            // if( data_map.count(t) > 0 ) { //berks__old\n            //     data_map.at( t )->setImageFromMsg( img_1_msg, 1 );\n            // }\n            if( data_map->count(t) > 0 ) {\n                img_data_mgr->setNewImageFromMsg( \"depth_image\", depth_image_msg );\n\n                depth_im_buf.pop();\n            }\n            else {\n                ros::Time newest_T = data_map->rbegin()->first;\n                __DataManager__data_association_thread__( cout << \"newest_T=\" << newest_T << endl; ) ;\n                if( newest_T.toNSec() > t.toNSec() )\n                {\n                    cout << \"newest_T is ahead of the t, so just drop this depth-image\";\n                    depth_im_buf.pop();\n                } else {\n                // assert( false && \"[DataManager::data_association_thread] attempting to set additional image into datanode. However that datanode is not found in the map. This cannot be happening\\n\");\n                __DataManager__data_association_thread__(\n                    cout << TermColor::RED() << \"[DataManager::data_association_thread]\";\n                    cout << \"attempting to set additional depth_image with t=\" << t << \" aka \" << t-pose_0 <<  \" into datanode. \";\n                    cout << \"However that datanode is not found in the map. This cannot be happening, but might happen when `depth_image` preceeds (even a few milis) than `image`.\";\n                    cout << TermColor::RESET() << endl ;\n\n                cout << TermColor::CYAN() << \"so dont pop from queue.\\n\" << TermColor::RESET();\n                )\n                break;\n                }\n            }\n        }\n\n\n        // dequeue all poses and set them to data_map\n        while( pose_buf.size() > 0 ) {\n            nav_msgs::Odometry::ConstPtr pose_msg = pose_buf.front();\n\n             ros::Time t = pose_msg->header.stamp;\n             __DataManager__data_association_thread__(\n             cout << \">> Attempt adding poped() pose in data_map with t=\" << pose_msg->header.stamp << \" ie. #####> \" << pose_msg->header.stamp -pose_0 << endl;\n             )\n\n             // find the DataNode with this timestamp\n             //berks__old\n            //  if( data_map.count( t ) > 0 ) {\n            //      // a Node seem to exist with this t.\n            //      data_map.at( t )->setPoseFromMsg( pose_msg );\n            //  }\n             if( data_map->count( t ) > 0 ) {\n                 // a Node seem to exist with this t.\n                 data_map->at( t )->setPoseFromMsg( pose_msg );\n                 pose_buf.pop();\n             }\n             else {\n                 if( t > data_map->rbegin()->first ) {\n                     __DataManager__data_association_thread__(\n                         cout << \"\\tpose's t was not yet found in datamap. data_map->rbegin()->first=\" << data_map->rbegin()->first << \" \";\n                         cout << \"this means a node doesnt exists yet for this pose. Usually this does not happen, but it occurs when Image data manager took too long to insert (thread blocking) and in the meantime more poses got available.\\nI will not pop the queue in this.\\n\";\n                     );\n                     break;\n                 }\n\n                 // try range search\n                 // t-delta <= x <= t+delta. x is the map key.\n\n                 __DataManager__data_association_thread__( cout << \"\\tsince the key (for associating pose with data_map) was not found in data_map do range_search\\n\"; )\n                 //berks__old\n                 auto __it = data_map->begin();\n                 for( __it = data_map->begin() ; __it != data_map->end() ; __it++ ) {\n                     ros::Duration diff =  __it->first-t;\n                     if( (diff.sec == 0  &&  abs(diff.nsec) < 1000000) || (diff.sec == -1  &&  diff.nsec > (1000000000-1000000) )  )\n                        break;\n                 }\n\n                 // berks__old\n                 if( __it == data_map->end() ) {\n                     cout << TermColor::RED() << \"\\t`data_association_thread`:pose:(not fouind) range search failed AAA FATAL \\n\";\n                     assert( false && \"\\tnot fouind\\n\");\n                     exit(2);\n                 }\n\n                __DataManager__data_association_thread__(\n                 cout << \"\\tfind pose->t=\" << t << \" in data_map\\n\";\n                 std::cout << \"\\tinsert at position \" << (__it->first) << '\\n';\n                )\n\n                if( true )\n                 {\n                    // berks__old\n                     data_map->at( __it->first )->setPoseFromMsg( pose_msg );\n                     pose_buf.pop();\n                 }\n                 else {\n\n                 cout << TermColor::RED() << \"[DataManager::data_association_thread] data_map does not seem to contain the t of pose_msg. This cannot be happening...fatal quit\" << TermColor::RESET() << endl;\n                 assert( false && \"[DataManager::data_association_thread] data_map does not seem to contain the t of pose_msg. This cannot be happening\\n\");\n                 exit(2);\n                }\n\n            } // else of if (data_map->count(t) > 0\n         }\n\n\n        // dequeue all point clouds (these are at keyframes)\n        while( ptcld_buf.size() > 0 ) {\n            sensor_msgs::PointCloudConstPtr ptcld_msg = ptcld_buf.front();\n\n\n            ros::Time t = ptcld_msg->header.stamp;\n            __DataManager__data_association_thread__(\n            cout << \">> Attempt adding poped() pointcloud in data_map at t=\" << t << \" ie. #####> \" <<  t - pose_0 << endl;\n            )\n\n            // find the DataNode with this timestamp\n            // berks__old\n            if( data_map->count( t ) > 0 ) {\n                // a Node seem to exist with this t.\n\n                //NOte: the ``/vins_estimator/keyframe_point`` and ``/feature_tracker/feature``\n                // are in different formats so be careful. vins_estimator/keyframe_point has n channels. while feature_tracker/feature has just 5 channels.\n                // but they both convey the same info, so be careful what exact you want.\n                #if 0\n                data_map.at( t )->setPointCloudFromMsg( ptcld_msg );\n                data_map.at( t )->setUnVnFromMsg( ptcld_msg );\n                data_map.at( t )->setUVFromMsg( ptcld_msg );\n                data_map.at( t )->setTrackedFeatIdsFromMsg( ptcld_msg );\n                data_map.at( t )->setAsKeyFrame();\n                ptcld_buf.pop();\n                #else\n                __DataManager__data_association_thread__(\n                cout << \"setNumberOfSuccessfullyTrackedFeatures \" << t << \" \" << ptcld_msg->points.size() << endl;\n                )\n                //berks__old\n                data_map->at( t )->setNumberOfSuccessfullyTrackedFeatures( ptcld_msg->points.size() );\n                data_map->at( t )->setAsKeyFrame();\n                ptcld_buf.pop();\n                #endif\n            }\n            else {\n                if( t > data_map->rbegin()->first ) {\n                    __DataManager__data_association_thread__(\n                        cout << \"ptcld's t was not yet found in datamap. data_map->rbegin()->first=\" << data_map->rbegin()->first << \" \";\n                        cout << \"this means a node doesnt exists yet for this ptcld. Usually this does not happen, but it occurs when Image data manager took too long to insert (thread blocking) and in the meantime more poses got available.\\n\\tI will not pop the queue in this.\\n\";\n                    );\n                    break;\n                }\n\n                // try range search\n                // t-delta <= x <= t+delta. x is the map key.\n                __DataManager__data_association_thread__( cout << \"\\tsince the key was not found (for ptcld) in data_map do range_search\\n\"; )\n\n                // berks__old\n                auto __it = data_map->begin();\n                for( __it = data_map->begin() ; __it != data_map->end() ; __it++ ) {\n                    ros::Duration diff =  __it->first-t;\n                    if( (diff.sec == 0  &&  abs(diff.nsec) < 1000000) || (diff.sec == -1  &&  diff.nsec > (1000000000-1000000) )  )\n                       break;\n                }\n\n                // berks__old\n                if( __it == data_map->end() ) {\n                    cout << TermColor::RED() << \"\\trange search failed BBB. if this occurs please report to authors (Manohar Kuse)\\n\";\n                    assert( false && \"\\tnot fouind\\n\");\n                    exit(2);\n                }\n\n               __DataManager__data_association_thread__(\n                cout << \"\\tfind ptcld->t=\" << t << \" in data_map\\n\";\n                std::cout << \"\\tinsert ptcld at position \" << (__it->first) << '\\n';\n               )\n\n\n               if( true ) {\n                   #if 0\n                   data_map.at( __it->first )->setPointCloudFromMsg( ptcld_msg );\n                   data_map.at( __it->first )->setUnVnFromMsg( ptcld_msg );\n                   data_map.at( __it->first )->setUVFromMsg( ptcld_msg );\n                   data_map.at( __it->first )->setTrackedFeatIdsFromMsg( ptcld_msg );\n                   data_map.at( __it->first )->setAsKeyFrame();\n                   ptcld_buf.pop();\n                   #else\n                   //berks__old\n                   data_map->at( __it->first )->setNumberOfSuccessfullyTrackedFeatures( ptcld_msg->points.size() );\n                   data_map->at( __it->first )->setAsKeyFrame();\n                   ptcld_buf.pop();\n                   #endif\n               }\n               else {\n                    __DataManager__data_association_thread__(cout << TermColor::RED() << \"[DataManager::data_association_thread] data_map does not seem to contain the t of ptcld_msg. This cannot be happening.\" << TermColor::RESET() << endl ;)\n                    assert( false && \"[DataManager::data_association_thread] data_map does not seem to contain the t of ptcld_msg. This cannot be happening\\n\");\n                    exit(2);\n                }\n            }\n        }\n\n\n        // Deal with cam_imu_extrinsic. Store the last in global variable of this class.\n        bool flag = false;\n        nav_msgs::OdometryConstPtr __msg;\n        while( extrinsic_cam_imu_buf.size() > 0 ) {\n            // dump all\n            __msg = extrinsic_cam_imu_buf.front();\n            extrinsic_cam_imu_buf.pop();\n            flag = true;\n        }\n        if( flag ) {\n            std::lock_guard<std::mutex> lk(global_vars_mutex);\n            PoseManipUtils::geometry_msgs_Pose_to_eigenmat( __msg->pose.pose, imu_T_cam );\n            imu_T_cam_available = true;\n            imu_T_cam_stamp = __msg->header.stamp;\n        }\n\n\n\n\n\n        //--------------------------- Done processing--------------------------\n        auto loop_end_at = std::chrono::high_resolution_clock::now();\n        std::chrono::duration<double, std::milli> ellapsed = loop_end_at-loop_start_at;\n\n        // sleep_for( requested_loop_time - ellapsed )\n        int sleep_for = int(requested_loop_time_ms - (float) ellapsed.count()) ;\n        __DataManager__data_association_thread__( cout << \"Loop iteration done in \"<< ellapsed.count() << \" ms; sleep_for=\" << sleep_for << \" ms\" << endl;  )\n\n        if( sleep_for > 0 )\n            std::this_thread::sleep_for( std::chrono::milliseconds( sleep_for )  );\n        else {\n            __DataManager__data_association_thread__( cout << \"Queueing in thread `data_association_thread`\\n\" ; )\n            ROS_WARN( \"Queueing in thread `data_association_thread`. requested_loop_time_ms=%f; elapsed=%f. If this occurs occasionally, it might be because of image_manager thread getting blocked by cleanup thread. It is perfectly normal.\", requested_loop_time_ms, ellapsed.count() );\n        }\n\n    }\n\n    cout << TermColor::RED() << \"[DataManager::data_association_thread] Finished thread\" << TermColor::RESET() << endl;\n\n}\n\n\n\n\n// will have DataNodes and ImageDataManager to disk for reloading later.\n// Camera related info is not to be stored. Just assume next time the camera relatives are the same as the map\nbool DataManager::saveStateToDisk( const string save_folder_name )\n{\n    cout << TermColor::GREEN();\n    cout << \"\\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n\";\n    cout << \"^^^^^^^^^^    DataManager::saveStateToDisk  ^^^^^^^^^^\\n\";\n    cout << \"^^^^^^^^^^    DIR=\" << save_folder_name ;\n    cout << \"\\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n\";\n    cout << TermColor::RESET();\n\n    // TODO: rm -rf save_folder_name ; mkdir save_folder_name\n    string system_cmd0 = string( \"rm -rf \") + save_folder_name + \" && mkdir \"+ save_folder_name;\n    const int rm_dir_err0 = RawFileIO::exec_cmd( system_cmd0 );\n    if ( rm_dir_err0 == -1 )\n    {\n        cout << TermColor::RED() << \"[DataManager::saveStateToDiskr] Cannot mkdir folder: \" << save_folder_name << \"!\\n\" << TermColor::RESET() << endl;\n        cout << \"So not saveing state to disk...return false\\n\";\n        return false;\n    }\n\n    //\n    // ---- Save std::map\n    json x_datamap;\n    auto __data_map = this->getDataMapRef();\n    IOFormat CSVFormat(FullPrecision, DontAlignCols, \", \", \"\\n\");\n\n    cout << \"data_map to json\\n\";\n    int n_isPoseAvailable = 0;\n    int n_isWholeImageDescriptorAvailable = 0;\n    int n_keyframes = 0;\n    for( auto it = __data_map->begin() ; it!= __data_map->end() ; it++ )\n    {\n        int seq_id = std::distance( __data_map->begin() , it );\n        // cout << \"seq_id=\" << seq_id << endl;\n\n        json curr_obj;\n        // curr_obj[\"stamp\"] = it->first.toSec();\n        curr_obj[\"stampNSec\"] = it->first.toNSec();\n        curr_obj[\"stamp_relative\"] =  ( it->first -  this->getPose0Stamp() ).toSec() ;\n        curr_obj[\"seq\"] = seq_id;\n\n\n        DataNode * __u = it->second;\n        if( __u->isPoseAvailable() )\n        {\n            const Matrix4d wTc = __u->getPose();\n            json pose_ifo;\n            pose_ifo[\"rows\"] = wTc.rows();\n            pose_ifo[\"cols\"] = wTc.cols();\n            // pose_ifo[\"stamp\"] = __u->getT_pose().toSec();\n            pose_ifo[\"stampNSec\"] = __u->getT_pose().toNSec();\n            std::stringstream ss;\n            ss <<  wTc.format(CSVFormat);\n            pose_ifo[\"data\"] = ss.str();\n            pose_ifo[\"data_pretty\"] = PoseManipUtils::prettyprintMatrix4d(wTc);\n            curr_obj[\"w_T_c\"] = pose_ifo;\n            n_isPoseAvailable++;\n        }\n\n\n        if( __u->isWholeImageDescriptorAvailable() )\n        {\n            const VectorXd wh_img_desc = __u->getWholeImageDescriptor();\n            json desc_ifo;\n            desc_ifo[\"rows\"] = wh_img_desc.rows();\n            desc_ifo[\"cols\"] = wh_img_desc.cols();\n            std::stringstream ss;\n            ss <<  wh_img_desc.format(CSVFormat);\n            desc_ifo[\"data\"] = ss.str();\n            curr_obj[\"wholeImageDescriptor\"] = desc_ifo;\n            n_isWholeImageDescriptorAvailable++;\n        }\n\n        if(  __u->isKeyFrame() )\n            n_keyframes++;\n\n        curr_obj[\"isKeyFrame\"] = __u->isKeyFrame();\n        curr_obj[\"getNumberOfSuccessfullyTrackedFeatures\"] = __u->getNumberOfSuccessfullyTrackedFeatures();\n        curr_obj[\"isWholeImageDescriptorAvailable\"] = __u->isWholeImageDescriptorAvailable();\n        curr_obj[\"isPoseAvailable\"] = __u->isPoseAvailable();\n\n        x_datamap[\"DataNodes\"].push_back( curr_obj );\n\n    }\n    cout << \"\\t n_isPoseAvailable=\" << n_isPoseAvailable << endl;\n    cout << \"\\t n_isWholeImageDescriptorAvailable=\" << n_isWholeImageDescriptorAvailable << endl;\n    cout << \"\\t n_keyframes=\" << n_keyframes << endl;\n    cout << \"DONE, data_map to json\\n\";\n\n    //\n    // --- Misc Variables in class DataManager\n\n    //\n    // --- save img_data_mgr\n    // - go over all status and stash all the images that remain on RAM.\n    // - save ImageDataManager::status to json\n    cout << \"img_data_mgr->stashAll()\\n\";\n    json img_mgr_json = img_data_mgr->stashAll();\n    cout << \"DONE, img_data_mgr->stashAll()\\n\";\n    x_datamap[\"ImageDataManager\"] = img_mgr_json;\n\n    // mv stash dir to `save_folder_name`\n    string system_cmd = string(\"mv \") +  img_data_mgr->getStashDir() + \" \" + save_folder_name;\n    const int rm_dir_err = RawFileIO::exec_cmd( system_cmd );\n    if ( rm_dir_err == -1 )\n    {\n        cout << TermColor::RED() << \"[DataManager::saveStateToDiskr] Error moving stash directory!\\n\" << TermColor::RESET() << endl;\n    }\n    img_data_mgr->set_rm_stash_dir_in_destructor_as_false();\n\n\n    //\n    // --- Save JSON\n    cout << \"x_datamap[\\\"DataNodes\\\"].size()=\" << x_datamap[\"DataNodes\"].size() << endl;\n    cout << \"x_datamap[\\\"ImageDataManager\\\"].size()=\" << x_datamap[\"ImageDataManager\"].size() << endl;\n    RawFileIO::write_string( save_folder_name+\"/state.json\", x_datamap.dump(4) );\n    cout << \"DONE........    DataManager::saveStateToDisk  ^^^^^^^^^^\\n\";\n    return true;\n}\n\n\nbool DataManager::loadStateFromDisk( const string save_folder_name )\n{\n    cout << TermColor::GREEN();\n    cout << \"\\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n\";\n    cout << \"^^^^^^^^^^    DataManager::loadStateFromDisk  ^^^^^^^^^^\\n\";\n    cout << \"^^^^^^^^^^    DIR=\" << save_folder_name ;\n    cout << \"\\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\\n\";\n    cout << TermColor::RESET();\n\n    //---\n    // Load JSON\n    string json_fname = save_folder_name+\"/state.json\";\n    cout << TermColor::GREEN() << \"[DataManager::loadStateFromDisk]Open file: \" << json_fname << TermColor::RESET() <<  endl;\n    std::ifstream json_fileptr(json_fname);\n    if( !json_fileptr )\n    {\n        ROS_ERROR( \"[DataManager::loadStateFromDisk]Cannot load from previous state\" );\n        cout << TermColor::RED() << \"[DataManager::loadStateFromDisk]Fail to open state.json file, perhaps it doesnt exist. Cannot load from previous state.\\nEXIT(1)\"<< endl;\n        exit(1);\n    }\n    json json_obj;\n    json_fileptr >> json_obj;\n    cout << \"[DataManager::loadStateFromDisk]Successfully opened file and loaded data \"<< json_fname << endl;\n    json_fileptr.close();\n\n    //---\n    // Setup DataNodes\n    int n_datanodes = json_obj[\"DataNodes\"].size();\n    int n_isPoseAvailable = 0;\n    int n_isWholeImageDescriptorAvailable = 0;\n    int n_keyframes = 0;\n    cout << \"[DataManager::loadStateFromDisk]There appear to be \" << n_datanodes << \" datanodes\\n\";\n    for( int i=0 ; i<n_datanodes ; i++ )\n    {\n\n        // double t_sec = json_obj[\"DataNodes\"][i][\"stamp\"];\n        int64_t t_sec = json_obj[\"DataNodes\"][i][\"stampNSec\"];\n        ros::Time stamp = ros::Time().fromNSec( t_sec );\n        bool isKeyframe = json_obj[\"DataNodes\"][i][\"isKeyFrame\"];\n        bool isWholeImageDescriptorAvailable = json_obj[\"DataNodes\"][i][\"isWholeImageDescriptorAvailable\"];\n        bool isPoseAvailable = json_obj[\"DataNodes\"][i][\"isPoseAvailable\"];\n        int numberOfSuccessfullyTrackedFeatures = json_obj[\"DataNodes\"][i][\"getNumberOfSuccessfullyTrackedFeatures\"];\n\n        if( i%100 == 0 || i==n_datanodes-1 )\n            cout << \"[DataManager::loadStateFromDisk]i=\" << i << \" of \"<< n_datanodes-1 << \" t=\" << stamp << endl;\n\n\n        DataNode * n = new DataNode( stamp );\n\n        // isKeyframe?\n        if( isKeyframe ) {\n            n_keyframes++;\n            n->setAsKeyFrame();\n        }\n\n        // number of tracked features\n        if( numberOfSuccessfullyTrackedFeatures >= 0 )\n            n->setNumberOfSuccessfullyTrackedFeatures( numberOfSuccessfullyTrackedFeatures );\n\n\n        // pose\n        if( isPoseAvailable )\n        {\n            // setPose()\n            Matrix4d __wtc;\n            // cout << \"+++\\n\" << json_obj[\"DataNodes\"][i][\"w_T_c\"] << \"\\n+++\" << endl;\n            bool status = RawFileIO::read_eigen_matrix4d_fromjson(  json_obj[\"DataNodes\"][i][\"w_T_c\"], __wtc  );\n            if( status == false ) {\n                cout << \"[DataManager::loadStateFromDisk] RawFileIO::read_eigen_matrix4d_fromjson returned false, this means I cannot convert from json to Matrix4d, perhaps something wrong with json file\\n\";\n                exit(1);\n            }\n            // cout << \"__wtc=\\n\" << __wtc << endl;\n            // cout << PoseManipUtils::prettyprintMatrix4d( __wtc );\n            // exit(1);\n\n            // double t_pose_sec = json_obj[\"DataNodes\"][i][\"w_T_c\"][\"stamp\"];\n            int64_t t_pose_sec =  json_obj[\"DataNodes\"][i][\"w_T_c\"][\"stampNSec\"];\n\n            ros::Time stamp_pose = ros::Time().fromNSec( t_pose_sec );\n            n->setPose(  stamp_pose,__wtc );\n\n            n_isPoseAvailable++;\n        }\n\n\n        // descriptor\n        if( isWholeImageDescriptorAvailable )\n        {\n            // setWholeImageDescriptor()\n            VectorXd __wholeImageDescriptor;\n            // cout << \"+++\\n\" << json_obj[\"DataNodes\"][i][\"wholeImageDescriptor\"] << \"\\n+++\" << endl;\n            bool status = RawFileIO::read_eigen_vector_fromjson(  json_obj[\"DataNodes\"][i][\"wholeImageDescriptor\"], __wholeImageDescriptor );\n            if( status == false ) {\n                cout << \"[DataManager::loadStateFromDisk] RawFileIO::read_eigen_vector_fromjson returned false, this means I cannot convert from json to Matrix4d, perhaps something wrong with json file\\n\";\n                exit(1);\n            }\n\n            // cout << \"__wholeImageDescriptor=\\n\" << __wholeImageDescriptor.transpose() << endl;\n            // exit(1);\n\n            n->setWholeImageDescriptor( __wholeImageDescriptor );\n            n_isWholeImageDescriptorAvailable++;\n        }\n\n\n        data_map->insert( std::make_pair(stamp, n) );\n    }\n    cout << \"\\t n_isPoseAvailable=\" << n_isPoseAvailable << endl;\n    cout << \"\\t n_isWholeImageDescriptorAvailable=\" << n_isWholeImageDescriptorAvailable << endl;\n    cout << \"\\t n_keyframes=\" << n_keyframes << endl;\n\n\n    //---\n    // Misc Variables in class DataManager\n\n\n    //---\n    // Setup ImageDataManager\n    // mv folder to /tmp/...\n    string system_cmd = string(\"cp -r \")+save_folder_name+\"/cerebro_stash \"+\" /tmp\";\n    // string system_cmd = string(\"mv  \")+save_folder_name+\"/cerebro_stash \"+\" /tmp\";\n    cout << TermColor::YELLOW() << \"[DataManager::loadStateFromDisk] \" << system_cmd << TermColor::RESET() << endl;\n    const int mv_dir_err = RawFileIO::exec_cmd( system_cmd );\n    if ( mv_dir_err == -1 )\n    {\n        cout << TermColor::RED() << \"[DataManager::loadStateFromDisk] Error moving directory!\\n\" << TermColor::RESET() << endl;\n        exit(1);\n    }\n\n    img_data_mgr->initStashDir(false);\n    img_data_mgr->loadStateFromDisk( json_obj[\"ImageDataManager\"] );\n\n\n\n\n}\n"
  },
  {
    "path": "src/DataManager.h",
    "content": "#pragma once\n\n/** DataManager - Inmemory database of data from VINS-estimator\n\n        This attempts to store all the data from VINS-estimator\n        in a in-memory database. Note that this has multiple threads.\n\n        The data will be maintained as a std::map with key as timestamp,\n        value as custom class DataNode. Other global data will also be kept.\n        Callbacks are also handled here.\n\n        Author  : Manohar Kuse <mpkuse@connect.ust.hk>\n        Created : 3rd Oct, 2017\n        Major Update : Jun, 2018\n        Major Update : Oct, 2018\n\n**/\n\n\n#include <iostream>\n#include <string>\n#include <vector>\n#include <fstream>\n#include <queue>\n#include <ostream>\n#include <iomanip>\n#include <map>\n#include <iterator>\n#include <ctime>\n\n\n#include <thread>\n#include <mutex>\n#include <atomic>\n#include <chrono>\n#include <memory> //needed for std::shared_ptr\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <cv_bridge/cv_bridge.h>\n\n// ros\n#include <ros/ros.h>\n#include <ros/package.h>\n\n// ros msg\n#include <nav_msgs/Odometry.h>\n#include <geometry_msgs/Pose.h>\n#include <geometry_msgs/PoseWithCovariance.h>\n#include <geometry_msgs/PoseStamped.h>\n#include <sensor_msgs/PointCloud.h>\n#include <geometry_msgs/Point32.h>\n#include <sensor_msgs/Image.h>\n// #include <nav_msgs/Path.h>\n#include <geometry_msgs/Point.h>\n#include <visualization_msgs/Marker.h>\n#include <visualization_msgs/MarkerArray.h>\n#include <std_msgs/Bool.h>\n\n\n// Eigen\n#include <Eigen/Core>\n#include <Eigen/Dense>\n#include <Eigen/Geometry>\nusing namespace Eigen;\n#include <opencv2/core/eigen.hpp>\n\n#include \"PinholeCamera.h\"\n#include \"camodocal/camera_models/Camera.h\" // this is in include/camodocal. src files in src/utils/camodocal_src\n#include \"camodocal/camera_models/CameraFactory.h\"\n\n#include \"DataNode.h\"\n#include \"ImageDataManager.h\"\n\n#include \"utils/nlohmann/json.hpp\"\nusing json = nlohmann::json;\n\n\n#include \"utils/PoseManipUtils.h\"\n#include \"utils/RawFileIO.h\"\n#include \"utils/SafeQueue.h\"\n#include \"utils/ElapsedTime.h\"\n\ntypedef std::map< ros::Time, DataNode* > t__DataNode;\n\nclass DataManager\n{\npublic:\n    DataManager( ros::NodeHandle &nh );\n    DataManager(const DataManager &obj);\n\n    // void setCamera( const PinholeCamera& camera );\n    // PinholeCamera& getCameraRef() { return camera;}\n\n    void setAbstractCamera( camodocal::CameraPtr abs_camera, short cam_id=0 );\n    camodocal::CameraPtr getAbstractCameraRef(short cam_id=0) const;\n    bool isAbstractCameraSet(short cam_id=0) const;\n    vector<short> getAbstractCameraKeys() const;\n\n    void setCameraRelPose( Matrix4d a_T_b, std::pair<int,int> pair_a_b );\n    bool isCameraRelPoseSet( std::pair<int,int> pair_a_b ) const;\n    const Matrix4d& getCameraRelPose( std::pair<int,int> pair_a_b ) const;\n    vector< std::pair<int,int> > getCameraRelPoseKeys();\n\n    // std::map< ros::Time, DataNode* >& getDataMapRef() { return data_map; }\n    std::shared_ptr< t__DataNode > getDataMapRef() { return data_map; }\n\n    std::shared_ptr< ImageDataManager > getImageManagerRef() { return img_data_mgr; }\n\n    const ros::Time getPose0Stamp() const { return pose_0; }\n    bool isPose0Available() const { return pose_0_available; }\n\n    const Matrix4d& getIMUCamExtrinsic() const;\n    bool isIMUCamExtrinsicAvailable() const;\n    const ros::Time getIMUCamExtrinsicLastUpdated() const;\n\n\n    // generally set fname something like /dev/pts/20, output to a separate terminal.\n    // you can know a terminal's device name with the command `tty`\n    void print_datamap_status( string fname ) const;\n\npublic:\n    ////////\n    /////// Kidnap Indicator Publisher\n    ///////\n\n    // publish true and publish false.\n    // The timestamps indicate the start of kidnap and end of kidnap respectively.\n    // each of the function will publish messages on '/feature_tracker/rcvd_flag'\n    // and on '/feature_tracker/rcvd_flag_header'\n\n    bool isKidnapIndicatorPubSet() const;\n    void setKidnapIndicatorPublishers( ros::Publisher& pub_bool, ros::Publisher& pub_header );\n    void PUBLISH__TRUE( const ros::Time _t ) const;\n    void PUBLISH__FALSE( const ros::Time _t ) const;\n\n\nprivate:\n    ros::Publisher rcvd_flag_pub;\n    ros::Publisher kidnap_indicator_header_pub;\n    bool is_kidnapn_indicator_set = false;\n\n\npublic:\n    ////////\n    //////// Write Data\n    ////////\n    // returns string as a json. contains everything including wTc, wX, uv, K, D etc.\n    json asJson() ;\n\n    bool saveStateToDisk( const string save_folder_name );\n    bool loadStateFromDisk( const string save_folder_name );\n\n\nprivate:\n    /////////\n    ///////// Global Variables\n    /////////\n    std::map< int, camodocal::CameraPtr > all_abstract_cameras;\n    std::map< std::pair<int,int>,  Matrix4d > cam_relative_poses; //< pair:a,b then a_T_b\n\n\n    ros::NodeHandle nh; //< Node Handle, TODO Not sure why this will be needed here. consider removing it from here.\n    // const std::ofstream &out_stream;\n\n    // std::map< ros::Time, DataNode* >  data_map; //original\n    std::shared_ptr< t__DataNode > data_map = std::make_shared<t__DataNode>();\n\n    std::shared_ptr< ImageDataManager> img_data_mgr = std::make_shared<ImageDataManager>();\n\n    bool pose_0_available = false;\n    ros::Time pose_0; // time of 1st pose\n\n    Matrix4d imu_T_cam = Matrix4d::Identity();\n    bool imu_T_cam_available = false;\n    ros::Time imu_T_cam_stamp;\n\n    mutable std::mutex global_vars_mutex;\n\n\npublic:\n    /////////\n    ///////// Callbacks\n    /////////\n    void camera_pose_callback( const nav_msgs::Odometry::ConstPtr msg ); ///< w_T_c. pose of camera in the world-cordinate system. All the cameras. only a subset of this will be keyframes\n    void keyframe_pose_callback( const nav_msgs::Odometry::ConstPtr msg ); //X /// pose of imu at keyframes. Use it just as a marker, dont use the poses.\n\n\n    void raw_image_callback( const sensor_msgs::ImageConstPtr& msg );\n    void raw_image_callback_1( const sensor_msgs::ImageConstPtr& msg );\n    void depth_image_callback( const sensor_msgs::ImageConstPtr& msg );\n\n    void extrinsic_cam_imu_callback( const nav_msgs::Odometry::ConstPtr msg );\n    void ptcld_callback( const sensor_msgs::PointCloud::ConstPtr msg );\n    void tracked_feat_callback( const sensor_msgs::PointCloud::ConstPtr msg ); //X\n\n\nprivate:\n    // double last_image_time=-1;\n    ros::Time last_image_time = ros::Time();\n\n    // callback-buffers\n    std::queue<sensor_msgs::ImageConstPtr> img_buf;\n    std::queue<sensor_msgs::ImageConstPtr> img_1_buf;\n    std::queue<sensor_msgs::ImageConstPtr> depth_im_buf;\n    std::queue<nav_msgs::OdometryConstPtr> pose_buf;\n    std::queue<nav_msgs::OdometryConstPtr> kf_pose_buf;\n    std::queue<sensor_msgs::PointCloudConstPtr> ptcld_buf;\n    std::queue<sensor_msgs::PointCloudConstPtr> trackedfeat_buf;\n    std::queue<nav_msgs::OdometryConstPtr> extrinsic_cam_imu_buf;\n\n    string print_queue_size(int verbose ) const;\n\n\n    /////////\n    ///////// Threads\n    /////////\npublic:\n    // This threads monitors the buffer queues and sync them all in a std::map which is indexed with time and holds all the data\n    void data_association_thread( int max_loop_rate_in_hz );\n    void data_association_thread_enable() { b_data_association_thread = true; }\n    void data_association_thread_disable() { b_data_association_thread = false; }\n\n\nprivate:\n    atomic<bool> b_data_association_thread;\n\n\npublic:\n    // Just a trial thread\n    void trial_thread();\n    void trial_thread_enable() { b_trial_thread = true; }\n    void trial_thread_disable() { b_trial_thread = false; }\n\nprivate:\n    atomic<bool> b_trial_thread;\n\n\npublic:\n    // Thread to deallocate images which have no pose info (aka useless images)\n    void clean_up_useless_images_thread();\n    void clean_up_useless_images_thread_enable() { b_clean_up_useless_images_thread = true; }\n    void clean_up_useless_images_thread_disable() { b_clean_up_useless_images_thread = false; }\n\nprivate:\n    atomic<bool> b_clean_up_useless_images_thread;\n\n\n};\n"
  },
  {
    "path": "src/DataNode.cpp",
    "content": "#include \"DataNode.h\"\n\n#if 0\nvoid DataNode::setImageFromMsg( const sensor_msgs::ImageConstPtr msg )\n{\n    std::lock_guard<std::mutex> lk(m);\n\n    auto tmp = cv_bridge::toCvShare(msg, \"bgr8\" )->image;\n\n    // this->image = cv_bridge::toCvShare(msg, \"bgr8\" )->image;\n    // this->image = (cv_bridge::toCvCopy(msg, \"bgr8\" )->image).clone();\n    // this->image = (cv_bridge::toCvShare(msg, \"bgr8\" )->image).clone();\n\n    this->image = tmp.clone();\n    tmp.release();\n\n\n    //\n    // Note: (on Opencv and cv_bridge, memory)\n    // I was observing a massive memory leak when not using clone().\n    // See [this link](https://answers.opencv.org/question/14285/how-to-free-memory-through-cvmat/)\n    // So, the point is, if using cv::Mat, it is important to understand who owns the memory.\n    // cv::Mat::clone() does a deep copy. it is important to .clone here for\n    // the deallocation to work correctly.\n\n\n    assert( image.rows > 0 && image.cols > 0 && !image.empty() && \"In DataNode::setImageFromMsg image that is being set from sensor_msgs::Image is invalid.\");\n\n\n    this->t_image = msg->header.stamp;\n    this->m_image = true;\n}\n\nvoid DataNode::setImageFromMsg( const sensor_msgs::ImageConstPtr msg, short cam_id )\n{\n    std::lock_guard<std::mutex> lk(m);\n\n    // cv::Mat __image = cv_bridge::toCvShare(msg, \"bgr8\" )->image;\n    cv::Mat __image = (cv_bridge::toCvCopy(msg, \"bgr8\" )->image).clone();\n    this->all_images[cam_id] = __image;\n\n    //\n    // Note: (on Opencv and cv_bridge, memory)\n    // I was observing a massive memory leak when not using clone().\n    // See [this link](https://answers.opencv.org/question/14285/how-to-free-memory-through-cvmat/)\n    // So, the point is, if using cv::Mat, it is important to understand who owns the memory.\n    // cv::Mat::clone() does a deep copy. it is important to .clone here for\n    // the deallocation to work correctly.\n\n    assert( this->all_images.at(cam_id).rows > 0 && this->all_images.at(cam_id).cols > 0 && ! this->all_images.at(cam_id).empty() && \"In DataNode::setImageFromMsg with cam_id image that is being set from sensor_msgs::Image is invalid.\");\n\n\n    this->t_all_images[cam_id] = msg->header.stamp;\n\n    __image.release();\n\n}\n\nvoid DataNode::print_image_cvinfo()\n{\n    std::lock_guard<std::mutex> lk(m);\n    cout << \"[DataNode::print_image_cvinfo]\\n\";\n\n    if( m_image ) {\n        cout << \"[DataNode::print_image_cvinfo] main image: \" << MiscUtils::cvmat_info( image ) << endl;\n        cout << TermColor::iBLUE() << \"[DataNode::print_image_cvinfo] refcount=\" << image.u->refcount << TermColor::RESET() << endl;\n    }\n\n    for( auto it=all_images.begin() ; it!=all_images.end() ; it++ ) {\n        cout << TermColor::iBLUE() << \"[DataNode::print_image_cvinfo] image#\" << it->first << \" refcount=\" << it->second.u->refcount << TermColor::RESET() << endl;\n    }\n\n    cout << \"[DataNode::print_image_cvinfo] Done\\n\";\n}\n\n// #define __DATANODE__deallocate_all_images( msg ) msg;\n#define __DATANODE__deallocate_all_images( msg ) ;\nvoid DataNode::deallocate_all_images()\n{\n    __DATANODE__deallocate_all_images(\n    cout << \"[DataNode::deallocate_all_images] getT= \" << getT() << \" getT_image()=\"<< getT_image() << \"\\n\";\n    )\n    std::lock_guard<std::mutex> lk(m);\n\n    // deallocate main image\n    if( m_image ) {\n        image.release();\n        image.deallocate();\n        t_image = ros::Time();\n        m_image = false;\n    }\n    // cout << \"[DataNode::deallocate_all_images] After Deallocate image.u->refcount=\" << image.u->refcount << endl;\n\n    // deallocate other images with cam_id\n    for( auto it=all_images.begin() ; it!=all_images.end() ; it++ ) {\n        it->second.release();\n        it->second.deallocate();\n        // cout << \"[DataNode::deallocate_all_images] image# \" << it->first << \", After Deallocate it->second.u->refcount=\" << image.u->refcount << endl;\n    }\n    all_images.clear();\n    t_all_images.clear();\n}\n#endif\n\nvoid DataNode::setPoseFromMsg( const nav_msgs::OdometryConstPtr msg )\n{\n    std::lock_guard<std::mutex> lk(m);\n\n    PoseManipUtils::geometry_msgs_Pose_to_eigenmat( msg->pose.pose, wTc );\n\n    // vector<double> _q = msg->pose.covariance ;\n    wTc_covariance = MatrixXd::Zero(6,6);\n    for( int i=0 ; i<36 ; i++ )\n        wTc_covariance(int(i/6), i%6) = msg->pose.covariance[i];\n\n    t_wTc = msg->header.stamp;\n    m_wTc = true;\n}\n\n\nvoid DataNode::setPose( const Matrix4d __wTc )\n{\n    std::lock_guard<std::mutex> lk(m);\n\n    wTc_covariance = MatrixXd::Zero(6,6);\n    wTc = Matrix4d( __wTc );\n\n    m_wTc = true;\n}\n\nvoid DataNode::setPose( const ros::Time __t, const Matrix4d __wTc )\n{\n    std::lock_guard<std::mutex> lk(m);\n\n    wTc_covariance = MatrixXd::Zero(6,6);\n    wTc = Matrix4d( __wTc );\n\n    t_wTc = __t;\n    m_wTc = true;\n}\n\n\nvoid DataNode::setPointCloudFromMsg( const sensor_msgs::PointCloudConstPtr msg )\n{\n    std::lock_guard<std::mutex> lk(m);\n\n    int n_pts = msg->points.size() ;\n    if( n_pts == 0 ) {\n        // cout << TermColor::RED() << \"[DataNode::setPointCloudFromMsg]npts=0\" << TermColor::RESET() << endl;\n        t_ptcld = msg->header.stamp;\n        m_ptcld = true;\n        m_ptcld_zero_pts = true;\n        return ;\n    }\n    assert( n_pts > 0 && \"[DataNode::setPointCloudFromMsg] The input pointcloud msg contains zero 3d points, ie. msg->points\\n\");\n\n    ptcld = MatrixXd::Zero( 4, n_pts );\n    for( int i=0 ; i<n_pts ; i++ ) {\n        ptcld( 0, i ) = msg->points[i].x;\n        ptcld( 1, i ) = msg->points[i].y;\n        ptcld( 2, i ) = msg->points[i].z;\n        ptcld( 3, i ) = 1.0;\n    }\n\n    t_ptcld = msg->header.stamp;\n    m_ptcld = true;\n}\n\n\nvoid DataNode::setUnVnFromMsg( const sensor_msgs::PointCloudConstPtr msg )\n{\n    std::lock_guard<std::mutex> lk(m);\n\n    int n_pts = msg->channels.size() ;\n    if( n_pts == 0 ) {\n        // cout << TermColor::RED() << \"[DataNode::setUnVnFromMsg]npts=0\" << TermColor::RESET() << endl;\n        t_unvn = msg->header.stamp;\n        m_unvn = true;\n        m_unvn_zero_pts = true;\n        return ;\n    }\n    assert( n_pts > 0 && \"[DataNode::setUnVnFromMsg] The input pointcloud msg contains zero unvn points, ie. msg->channels\\n\");\n\n    unvn = MatrixXd::Zero( 3, n_pts );\n    for( int i=0 ; i<n_pts ; i++ ) {\n        unvn( 0, i ) = msg->channels[i].values[0];\n        unvn( 1, i ) = msg->channels[i].values[1];\n        unvn( 2, i ) = 1.0;\n    }\n\n    t_unvn = msg->header.stamp;\n    m_unvn = true;\n}\n\n\nvoid DataNode::setUVFromMsg( const sensor_msgs::PointCloudConstPtr msg )\n{\n    std::lock_guard<std::mutex> lk(m);\n\n    int n_pts = msg->channels.size() ;\n    if( n_pts == 0 ) {\n        // cout << TermColor::RED() << \"[DataNode::setUVFromMsg]npts=0\" << TermColor::RESET() << endl;\n        t_uv = msg->header.stamp;\n        m_uv = true;\n        m_uv_zero_pts = true;\n        return ;\n    }\n    assert( n_pts > 0 && \"[DataNode::setUVFromMsg] The input pointcloud msg contains zero uv points, ie. msg->channels\\n\");\n\n    uv = MatrixXd::Zero( 3, n_pts );\n    for( int i=0 ; i<n_pts ; i++ ) {\n        uv( 0, i ) = msg->channels[i].values[2];\n        uv( 1, i ) = msg->channels[i].values[3];\n        uv( 2, i ) = 1.0;\n    }\n\n    t_uv = msg->header.stamp;\n    m_uv = true;\n}\n\nvoid DataNode::setTrackedFeatIdsFromMsg( const sensor_msgs::PointCloudConstPtr msg )\n{\n    std::lock_guard<std::mutex> lk(m);\n\n    int n_pts = msg->channels.size() ;\n    if( n_pts == 0 ) {\n        // cout << TermColor::RED() << \"[DataNode::setTrackedFeatIdsFromMsg]npts=0\" << TermColor::RESET() << endl;\n        t_tracked_feat_ids = msg->header.stamp;\n        m_tracked_feat_ids = true;\n        m_tracked_feat_ids_zero_pts = true;\n        return ;\n    }\n    assert( n_pts > 0 && \"[DataNode::setTrackedFeatIdsFromMsg] The input pointcloud msg contains zero tracked feature point ids, ie. msg->channels\\n\");\n\n    tracked_feat_ids = VectorXi::Zero( n_pts );\n    for( int i=0 ; i<n_pts ; i++ ) {\n        tracked_feat_ids( i ) = int( msg->channels[i].values[4] );\n    }\n\n    t_tracked_feat_ids = msg->header.stamp;\n    m_tracked_feat_ids = true;\n}\n\nvoid DataNode::setNumberOfSuccessfullyTrackedFeatures( int n )\n{\n    std::lock_guard<std::mutex> lk(m);\n    assert( n >= 0 && \"[DataNode::setNumberOfSuccessfullyTrackedFeatures] you cannot set a negative value\");\n    numberOfSuccessfullyTrackedFeatures = n;\n}\n\nint DataNode::getNumberOfSuccessfullyTrackedFeatures() const\n{\n    std::lock_guard<std::mutex> lk(m);\n    return numberOfSuccessfullyTrackedFeatures;\n}\n\n\n#if 0\nconst cv::Mat& DataNode::getImage() const {\n    std::lock_guard<std::mutex> lk(m);\n    assert( isImageAvailable() && \"[DataNode::getImage] you requested the image before setting it\");\n    return this->image;\n}\n\n\nconst cv::Mat& DataNode::getImage(short cam_id) const {\n    std::lock_guard<std::mutex> lk(m);\n    assert( isImageAvailable(cam_id) && \"[DataNode::getImage with cam_id] you requested the image before setting it\");\n    // return this->all_images[cam_id];\n    return this->all_images.at(cam_id);\n}\n#endif\n\nconst Matrix4d& DataNode::getPose() const{\n    std::lock_guard<std::mutex> lk(m);\n    assert( m_wTc && \"[DataNode::getPose] you requested the pose before setting it\");\n    #if 0\n    if( !m_wTc ) { cout << \"ASSERT ERROR \" << \"[DataNode::getPose]  you requested the pose before setting it\\n\"; }\n    #endif\n    return wTc;\n}\n\n\nconst MatrixXd& DataNode::getPoseCovariance() const {\n    std::lock_guard<std::mutex> lk(m);\n    assert( m_wTc && \"[DataNode::getPoseCovariance] you requested the pose-cov before setting it\");\n\n    return wTc_covariance;\n}\n\nconst MatrixXd& DataNode::getPointCloud() const {\n    std::lock_guard<std::mutex> lk(m);\n    assert( m_ptcld && \"[DataNode::getPointCloud] you requested the pointcloud before setting it\");\n    return ptcld;\n}\n\nconst MatrixXd& DataNode::getUnVn() const {\n     // returns a 3xN matrix\n     std::lock_guard<std::mutex> lk(m);\n     assert( m_unvn && \"[DataNode::getUnVn] you requested UnVn before setting it.\\n\" );\n     return unvn;\n }\n\nconst MatrixXd& DataNode::getUV() const {\n     // returns a 3xN matrix\n     std::lock_guard<std::mutex> lk(m);\n     assert( m_unvn && \"[DataNode::getUV] you requested UnVn before setting it.\\n\" );\n     return uv;\n }\n\nconst VectorXi& DataNode::getFeatIds() const  {\n     // return a N-vector\n     std::lock_guard<std::mutex> lk(m);\n     assert( m_unvn && \"[DataNode::getFeatIds] you requested UnVn before setting it.\\n\" );\n     return tracked_feat_ids;\n }\n\n int DataNode::nPts() const {\n     std::lock_guard<std::mutex> lk(m);\n     #if 0\n     if( m_tracked_feat_ids == false || m_tracked_feat_ids_zero_pts == true )\n        return 0;\n     return tracked_feat_ids.size();\n     #endif\n\n }\n\n const ros::Time DataNode::getT() const {\n     std::lock_guard<std::mutex> lk(m);\n     return stamp;\n }\n\n #if 0\n const ros::Time DataNode::getT_image() const {\n     std::lock_guard<std::mutex> lk(m);\n     assert( isImageAvailable() );\n     return t_image;\n }\n const ros::Time DataNode::getT_image(short cam_id) const {\n     std::lock_guard<std::mutex> lk(m);\n     assert( isImageAvailable(cam_id) );\n    //  return t_all_images[cam_id];\n     return t_all_images.at(cam_id);\n }\n#endif\n\n\n const ros::Time DataNode::getT_pose() const {\n     std::lock_guard<std::mutex> lk(m);\n     return t_wTc;\n }\n const ros::Time DataNode::getT_ptcld() const {\n     std::lock_guard<std::mutex> lk(m);\n     return t_ptcld;\n }\n const ros::Time DataNode::getT_unvn() const {\n     std::lock_guard<std::mutex> lk(m);\n     return t_unvn;\n }\n const ros::Time DataNode::getT_uv() const {\n     std::lock_guard<std::mutex> lk(m);\n     return t_uv;\n }\n\nvoid DataNode::prettyPrint()\n{\n    if( this->isKeyFrame() )\n        cout << \"\\033[1;32m\";\n    else\n        cout << \"\\033[1;31m\";;\n\n    cout << \"\\t---DataNode\";\n    cout << \"\\tt=\"<< this->getT() ;\n    cout << \"\\tkeyFrame=\" << (this->isKeyFrame()?\"YES\":\"NO\") << endl ;\n\n\n    cout << \"\\t\\twholeImageDesc: \";\n    if( this->isWholeImageDescriptorAvailable() ) {\n        cout << \" available and of size: \" << this->getWholeImageDescriptor().size() << endl;\n    }\n    else { cout << \"Not Available\\n\" ;}\n\n    #if 0\n    if( isImageAvailable() ) {\n        cv::Mat _im = this->getImage();\n        cout << \"\\t\\tImage: \" << _im.rows << \"x\" << _im.cols << \"x\" << _im.channels()\n             << \"  \" << MiscUtils::type2str( _im.type() ) << \"\\tt=\" << t_image << endl;\n\n    }\n    else { cout << \"\\tImage: N/A\\n\"; }\n    #endif\n\n    if( isPoseAvailable() ) {\n        Matrix4d _pose = this->getPose();\n        cout << \"\\t\\tPose : \" << PoseManipUtils::prettyprintMatrix4d( _pose ) << \"\\tt=\" << t_wTc << endl;\n    }\n    else { cout << \"\\t\\tPose: N/A\\n\"; }\n\n    if( isPtCldAvailable() ) {\n        MatrixXd _ptcld = this->getPointCloud();\n        cout << \"\\t\\tPointCloud : \" << _ptcld.rows() << \"x\" << _ptcld.cols()  << \"\\tt=\" << t_ptcld  << endl;\n    }\n    else { cout << \"\\t\\tPointCloud : N/A\\n\"; }\n\n    if( isUnVnAvailable() ) {\n        MatrixXd _unvn = this->getUnVn();\n        cout << \"\\t\\tUnVn : \" << _unvn.rows() << \"x\" << _unvn.cols() << \"\\tt=\" << t_unvn << endl;\n    }\n    else { cout << \"\\t\\tUnVn : N/A\\n\"; }\n\n    if( isUVAvailable() ) {\n        MatrixXd _uv = this->getUV();\n        cout << \"\\t\\tUV : \" << _uv.rows() << \"x\" << _uv.cols() << \"\\tt=\" << t_uv  << endl;\n    }\n    else { cout << \"\\t\\tUV : N/A\\n\"; }\n\n    if( isFeatIdsAvailable() ) {\n        VectorXi _ids = this->getFeatIds();\n        cout << \"\\t\\tFeatIds : \" << _ids.rows() << \"x\" << _ids.cols() << \"\\tt=\" << t_tracked_feat_ids << endl;\n    }\n    else { cout << \"\\t\\tFeatIds : N/A\\n\"; }\n    cout << \"\\033[0m\\n\";\n}\n\n\n\nvoid DataNode::setWholeImageDescriptor( VectorXd vec )\n{\n    std::lock_guard<std::mutex> lk(m);\n\n    assert( !m_img_desc && \"[DataNode::setWholeImageDescriptor] You are trying to set image desc. The descriptor is already set and this action might overwrite the exisiting descriptor. If you think this is not an error feel free to comment this assert\\n\" );\n    img_desc = vec;\n    m_img_desc = true;\n}\n\n// const VectorXd& DataNode::getWholeImageDescriptor()\nconst VectorXd DataNode::getWholeImageDescriptor() const\n{\n    std::lock_guard<std::mutex> lk(m);\n    assert( m_img_desc && \"[DataNode::getWholeImageDescriptor] You are trying to get the image descriptor before setting it.\" );\n\n    return img_desc;\n\n}\n"
  },
  {
    "path": "src/DataNode.h",
    "content": "#pragma once\n\n/** This class holds data at 1 instance of time.\n\n        Author  : Manohar Kuse <mpkuse@connect.ust.hk>\n        Created : 27th Oct, 2018\n\n**/\n\n#include <iostream>\n#include <thread>\n#include <mutex>\n#include <atomic>\n\n\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <cv_bridge/cv_bridge.h>\n\n// ros\n#include <ros/ros.h>\n#include <ros/package.h>\n\n// ros msg\n#include <nav_msgs/Odometry.h>\n#include <geometry_msgs/Pose.h>\n#include <geometry_msgs/PoseWithCovariance.h>\n#include <geometry_msgs/PoseStamped.h>\n#include <sensor_msgs/PointCloud.h>\n#include <geometry_msgs/Point32.h>\n#include <sensor_msgs/Image.h>\n// #include <nav_msgs/Path.h>\n#include <geometry_msgs/Point.h>\n#include <visualization_msgs/Marker.h>\n#include <visualization_msgs/MarkerArray.h>\n\n// Eigen\n#include <Eigen/Core>\n#include <Eigen/Dense>\n#include <Eigen/Geometry>\nusing namespace Eigen;\n#include <opencv2/core/eigen.hpp>\n\n#include \"utils/PoseManipUtils.h\"\n#include \"utils/MiscUtils.h\"\n#include \"utils/TermColor.h\"\nclass DataNode\n{\npublic:\n    DataNode( ros::Time stamp ): stamp(stamp)\n    {\n        is_key_frame = false;\n        // m_image=false;\n        m_wTc=false;\n    }\n\n    #if 0\n    void setImageFromMsg( const sensor_msgs::ImageConstPtr msg ); ///< this sets the primary image\n    void setImageFromMsg( const sensor_msgs::ImageConstPtr msg, short cam_id ); ///< this sets additional image. multiple additional images by Node is possible by using ids\n    #endif\n\n    void setPoseFromMsg( const nav_msgs::OdometryConstPtr msg );\n    void setPose( const Matrix4d __wTc );\n    void setPose( const ros::Time __t, const Matrix4d __wTc );\n\n    void setPointCloudFromMsg( const sensor_msgs::PointCloudConstPtr msg ); // uses msg->points[ \\forall i].x, .y, .z\n    void setUnVnFromMsg( const sensor_msgs::PointCloudConstPtr msg );\n    void setUVFromMsg( const sensor_msgs::PointCloudConstPtr msg );\n    void setTrackedFeatIdsFromMsg( const sensor_msgs::PointCloudConstPtr msg );\n\n    void setAsKeyFrame() { is_key_frame = true; }\n    void unsetAsKeyFrame() { is_key_frame = false; }\n\n    // This is intended to indicate the number of tracked features from /feature_tracker.\n    // TODO: In the future if need be implement to save the tracked points. For now I am not retaining those\n    void setNumberOfSuccessfullyTrackedFeatures( int n );\n    int getNumberOfSuccessfullyTrackedFeatures() const;\n\n\n    bool isKeyFrame()  const{ return (bool)is_key_frame; }\n    #if 0\n    bool isImageAvailable() const { return m_image; }\n    bool isImageAvailable(short cam_id) const { return ((t_all_images.count(cam_id)>0)?true:false); }\n    #endif\n    bool isPoseAvailable() const { return m_wTc; }\n    bool isPtCldAvailable() const { return m_ptcld; }\n    bool isUnVnAvailable()  const{ return m_unvn; }\n    bool isUVAvailable() const { return m_uv; }\n    bool isFeatIdsAvailable() const { return m_tracked_feat_ids; }\n\n    #if 0\n    const cv::Mat& getImage() const; //< this will give out the default image.\n    const cv::Mat& getImage(short cam_id) const ; // this will give out the image of the cam_id. cam_id=0 will have issues. if you want the default camera image do not pass any argument, which will result in the above call.\n    #endif\n    const Matrix4d& getPose() const ;\n    const MatrixXd& getPoseCovariance() const ; //6x6 matrix\n    const MatrixXd& getPointCloud() const ; // returns a 4xN matrix\n    const MatrixXd& getUnVn() const ; // returns a 3xN matrix\n    const MatrixXd& getUV() const ; // returns a 3xN matrix\n    const VectorXi& getFeatIds() const; // return a N-vector\n    int nPts() const;\n\n    const ros::Time getT() const;\n    #if 0\n    const ros::Time getT_image() const;\n    const ros::Time getT_image(short cam_id) const;\n    #endif\n    const ros::Time getT_pose() const;\n    const ros::Time getT_ptcld() const;\n    const ros::Time getT_unvn() const ;\n    const ros::Time getT_uv() const ;\n\n    // Whole Image descriptors setter and getter\n    void setWholeImageDescriptor( VectorXd vec );\n    // const VectorXd& getWholeImageDescriptor();\n    const VectorXd getWholeImageDescriptor() const; //don't know which one is more suitated to me. :(\n    bool isWholeImageDescriptorAvailable() const { return m_img_desc; }\n\n\n    void prettyPrint();\n    #if 0\n    void deallocate_all_images();\n    void print_image_cvinfo();\n    #endif\n\n\nprivate:\n    const ros::Time stamp;\n    mutable std::mutex m;\n\n    // bool is_key_frame = false;\n    std::atomic<bool> is_key_frame;\n\n    #if 0\n    // Raw Image\n    cv::Mat image;\n    ros::Time t_image;\n    // bool m_image=false; // TODO better make this atomic<bool>\n    std::atomic<bool> m_image;\n\n    // Additional Raw Images\n    std::map<short,cv::Mat> all_images;\n    std::map<short,ros::Time> t_all_images;\n    #endif\n\n\n    // Pose (odometry pose from vins estimator)\n    Matrix4d wTc;\n    MatrixXd wTc_covariance; // 6x6\n    ros::Time t_wTc;\n    // bool m_wTc=false;\n    std::atomic<bool> m_wTc;\n\n\n    // point cloud (3d data)\n    MatrixXd ptcld;\n    ros::Time t_ptcld;\n    std::atomic<bool> m_ptcld;\n    std::atomic<bool> m_ptcld_zero_pts;\n\n\n    // unvn - imaged points in normalized cords\n    MatrixXd unvn;\n    ros::Time t_unvn;\n    std::atomic<bool> m_unvn;\n    std::atomic<bool> m_unvn_zero_pts;\n\n\n    // uv - imaged points in observed cords.\n    MatrixXd uv;\n    ros::Time t_uv;\n    std::atomic<bool> m_uv;\n    std::atomic<bool> m_uv_zero_pts;\n\n\n    // tracked feat ids\n    VectorXi tracked_feat_ids;\n    ros::Time t_tracked_feat_ids;\n    std::atomic<bool> m_tracked_feat_ids;\n    std::atomic<bool> m_tracked_feat_ids_zero_pts;\n\n    int numberOfSuccessfullyTrackedFeatures = -1;\n\n    // Whole Image Descriptor\n    VectorXd img_desc;\n    bool m_img_desc = false;\n\n};\n"
  },
  {
    "path": "src/DlsPnpWithRansac.cpp",
    "content": "\n#include \"DlsPnpWithRansac.h\"\n\n\n#ifdef __USE_THEIASFM\n// Theia's ICP\n// [Input]\n//      uv_X: a 3d point cloud expressed in some frame-of-ref, call it frame-of-ref of `uv`\n//      uvd_Y: a 3d point cloud expressed in another frame-of-ref, call it frame-of-ref of `uvd`\n// [Output]\n//      uvd_T_uv: Relative pose between the point clouds\n// [Note]\n//      uv_X <---> uvd_Y\n// #define ____P3P_ICP_( msg ) msg;\n#define ____P3P_ICP_( msg ) ;\nfloat StaticTheiaPoseCompute::P3P_ICP( const vector<Vector3d>& uv_X, const vector<Vector3d>& uvd_Y,\n    Matrix4d& uvd_T_uv, string & p3p__msg )\n{\n    if( uv_X.size() < 20 ) {\n        ____P3P_ICP_( cout << \"[StaticTheiaPoseCompute::P3P_ICP] Too few input points. You provided \" << uv_X.size() << endl; )\n        return -1; // if you give me too few points, i return error.\n    }\n\n    // call this theia::AlignPointCloudsUmeyamaWithWeights\n    // void AlignPointCloudsUmeyamaWithWeights(\n    //     const std::vector<Eigen::Vector3d>& left,\n    //     const std::vector<Eigen::Vector3d>& right,\n    //     const std::vector<double>& weights, Eigen::Matrix3d* rotation,\n    //     Eigen::Vector3d* translation, double* scale)\n\n\n    p3p__msg = \"\";\n    Matrix3d ____R;\n    Vector3d ____t; double ___s=1.0;\n\n    ElapsedTime timer;\n    timer.tic();\n\n    #if 0\n    theia::AlignPointCloudsUmeyama( uv_X, uvd_Y, &____R, &____t, &___s ); // all weights = 1. TODO: ideally weights could be proportion to point's Z.\n    // theia::AlignPointCloudsICP( uv_X, uvd_Y, &____R, &____t ); // all weights = 1. TODO: ideally weights could be proportion to point's Z.\n\n    double elapsed_time_p3p = timer.toc_milli();\n\n    ____P3P_ICP_(\n    cout << \"___R:\\n\" << ____R << endl;\n    cout << \"___t: \" << ____t.transpose() << endl;\n    cout << \"___s: \" << ___s << endl;\n    )\n\n    uvd_T_uv = Matrix4d::Identity();\n    uvd_T_uv.topLeftCorner<3,3>() = ____R;\n    uvd_T_uv.col(3).topRows(3) = ____t;\n\n    ____P3P_ICP_(\n    cout << TermColor::GREEN() << \"p3p done in (ms) \" << elapsed_time_p3p << \"  p3p_ICP: {uvd}_T_{uv} : \" << PoseManipUtils::prettyprintMatrix4d( uvd_T_uv ) << TermColor::RESET() << endl;\n    )\n\n\n\n    if( min( ___s, 1.0/___s ) < 0.9 ) {\n        cout << TermColor::RED() << \"theia::AlignPointCloudsUmeyama scales doesn't look good;this usually implies that estimation is bad.        scale= \" << ___s << endl;\n        p3p__msg += \"p3p_ICP: scale=\" +to_string( ___s )+\" {uvd}_T_{uv} : \" + PoseManipUtils::prettyprintMatrix4d( uvd_T_uv );\n        p3p__msg += \"p3p done in (ms)\" + to_string(elapsed_time_p3p)+\";    theia::AlignPointCloudsUmeyama scales doesn't look good, this usually implies that estimation is bad. scale= \" + to_string(___s);\n        return -1;\n    }\n\n    p3p__msg += \"p3p done in (ms)\" + to_string(elapsed_time_p3p)+\";    p3p_ICP: {uvd}_T_{uv} : \" + PoseManipUtils::prettyprintMatrix4d( uvd_T_uv );\n    p3p__msg += \";weight=\"+to_string( min( ___s, 1.0/___s ) );\n    return  min( ___s, 1.0/___s );\n    #endif\n\n    // with ransac\n    timer.tic();\n    vector<CorrespondencePair_3d3d> data_r;\n    for( int i=0 ; i<uv_X.size() ; i++ )\n    {\n        CorrespondencePair_3d3d _data;\n        _data.a_X = uv_X[i];\n        _data.b_X = uvd_Y[i];\n        data_r.push_back( _data );\n    }\n\n    AlignPointCloudsUmeyamaWithRansac icp_estimator;\n    RelativePose best_rel_pose;\n\n    // set ransac params\n    theia::RansacParameters params;\n    params.error_thresh = 0.1;\n    params.min_inlier_ratio = 0.7;\n    params.max_iterations = 50;\n    params.min_iterations = 5;\n    params.use_mle = true;\n\n    theia::Ransac<AlignPointCloudsUmeyamaWithRansac> ransac_estimator( params, icp_estimator);\n    ransac_estimator.Initialize();\n\n    theia::RansacSummary summary;\n    ransac_estimator.Estimate(data_r, &best_rel_pose, &summary);\n    auto elapsed_time_p3p_ransac = timer.toc_milli();\n\n    uvd_T_uv = Matrix4d::Identity();\n    uvd_T_uv =  best_rel_pose.b_T_a ;\n\n\n    ____P3P_ICP_(\n    cout << TermColor::iGREEN() << \"ICP Ransac:\";\n    // for( int i=0; i<summary.inliers.size() ; i++ )\n        // cout << \"\\t\" << i<<\":\"<< summary.inliers[i];\n    cout << \"\\tnum_iterations=\" << summary.num_iterations;\n    cout << \"\\tconfidence=\" << summary.confidence;\n    cout << endl;\n    cout << \"best solution (ransac icp ) : \"<< PoseManipUtils::prettyprintMatrix4d( best_rel_pose.b_T_a ) << TermColor::RESET() << endl;\n    )\n    p3p__msg += \"ICP Ransac;\";\n    p3p__msg += \"    best solution (b_T_a) : \"+ PoseManipUtils::prettyprintMatrix4d( best_rel_pose.b_T_a ) + \";\";\n    p3p__msg += \"     #iterations=\"+to_string( summary.num_iterations );\n    p3p__msg += \"    confidence=\"+to_string( summary.confidence ) ;\n    p3p__msg += \"    icp_ransac_elapsetime_ms=\"+to_string( elapsed_time_p3p_ransac ) +\";\";\n    return summary.confidence;\n\n}\n\n// Computation of pose given 3d points and imaged points\n// [Input]\n//      w_X: 3d points in co-ordinate system `w`\n//      c_uv_normalized: normalized image co-ordinates of camera c. These are projections of w_X on the camera c\n// [Output]\n//      c_T_w: pose of the camera is actually the inverse of this matrix. Becareful.\n// #define ___P_N_P__( msg ) msg;\n#define ___P_N_P__( msg ) ;\nfloat StaticTheiaPoseCompute::PNP( const std::vector<Vector3d>& w_X, const std::vector<Vector2d>& c_uv_normalized,\n    Matrix4d& c_T_w,\n    string& pnp__msg )\n{\n    if( w_X.size() < 20 ) {\n        ___P_N_P__( cout << \"[StaticTheiaPoseCompute::PNP] Too few input points. You provided \" << w_X.size() << endl; )\n        return -1; // if you give me too few points, i return error.\n    }\n    pnp__msg = \"\";\n    ElapsedTime timer;\n    #if 0\n    //--- DlsPnp\n    std::vector<Eigen::Quaterniond> solution_rotations;\n    std::vector<Eigen::Vector3d> solution_translations;\n    timer.tic();\n    theia::DlsPnp( c_uv_normalized, w_X, &solution_rotations, &solution_translations  );\n    auto elapsed_dls_pnp = timer.toc_milli() ;\n    ___P_N_P__(\n    cout << elapsed_dls_pnp << \" : (ms) : theia::DlsPnp done in\\n\";\n    cout << \"solutions count = \" << solution_rotations.size() << \" \" << solution_translations.size() << endl;\n    )\n\n    if( solution_rotations.size() == 0 ) {\n        ___P_N_P__(\n        cout << TermColor::RED() << \" theia::DlsPnp returns no solution\" << TermColor::RESET() << endl;\n        )\n        pnp__msg = \" theia::DlsPnp returns no solution\";\n        return -1.;\n    }\n\n    if( solution_rotations.size() > 1 ) {\n        ___P_N_P__(\n        cout << TermColor::RED() << \" theia::DlsPnp returns multiple solution\" << TermColor::RESET() << endl;\n        )\n        pnp__msg =  \" theia::DlsPnp returns multiple solution\";\n        return -1.;\n    }\n\n    // retrive solution\n    c_T_w = Matrix4d::Identity();\n    c_T_w.topLeftCorner(3,3) = solution_rotations[0].toRotationMatrix();\n    c_T_w.col(3).topRows(3) = solution_translations[0];\n\n    ___P_N_P__(\n    // cout << \"solution_T \" << b_T_a << endl;\n    cout << TermColor::GREEN() << \"DlsPnp (c_T_w): \" << PoseManipUtils::prettyprintMatrix4d( c_T_w ) << TermColor::RESET() << endl;\n    // out_b_T_a = b_T_a;\n    )\n\n    pnp__msg +=  \"DlsPnp (c_T_w): \" + PoseManipUtils::prettyprintMatrix4d( c_T_w ) + \";\";\n    pnp__msg += \"  elapsed_dls_pnp_ms=\"+to_string(elapsed_dls_pnp)+\";\";\n\n    // return 1.0;\n    #endif\n\n\n    #if 1\n    //--- DlsPnpWithRansac\n    timer.tic();\n    // prep data\n    vector<CorrespondencePair_3d2d> data_r;\n    for( int i=0 ; i<w_X.size() ; i++ )\n    {\n        CorrespondencePair_3d2d _data;\n        _data.a_X = w_X[i];\n        _data.uv_d = c_uv_normalized[i];\n        data_r.push_back( _data );\n    }\n\n    // Specify RANSAC parameters.\n\n    DlsPnpWithRansac dlspnp_estimator;\n    RelativePose best_rel_pose;\n\n    // Set the ransac parameters.\n    theia::RansacParameters params;\n    params.error_thresh = 0.03;\n    params.min_inlier_ratio = 0.7;\n    params.max_iterations = 50;\n    params.min_iterations = 5;\n    params.use_mle = true;\n\n    // Create Ransac object, specifying the number of points to sample to\n    // generate a model estimation.\n    theia::Ransac<DlsPnpWithRansac> ransac_estimator(params, dlspnp_estimator);\n    // Initialize must always be called!\n    ransac_estimator.Initialize();\n\n    theia::RansacSummary summary;\n    ransac_estimator.Estimate(data_r, &best_rel_pose, &summary);\n\n    auto elapsed_dls_pnp_ransac=timer.toc_milli();\n    ___P_N_P__(\n    cout << elapsed_dls_pnp_ransac << \"(ms)!! DlsPnpWithRansac ElapsedTime includes data prep\\n\";\n    cout << \"Ransac:\";\n    // for( int i=0; i<summary.inliers.size() ; i++ )\n        // cout << \"\\t\" << i<<\":\"<< summary.inliers[i];\n    cout << \"\\tnum_iterations=\" << summary.num_iterations;\n    cout << \"\\tconfidence=\" << summary.confidence;\n    cout << endl;\n    cout << TermColor::GREEN() << \"best solution (ransac) : \"<< PoseManipUtils::prettyprintMatrix4d( best_rel_pose.b_T_a ) << TermColor::RESET() << endl;\n    )\n    pnp__msg +=  \"DlsPnpWithRansac (best_rel_pose.b_T_a): \" + PoseManipUtils::prettyprintMatrix4d( best_rel_pose.b_T_a ) + \";\";\n    pnp__msg += string(\"    num_iterations=\")+to_string(summary.num_iterations)+\"  confidence=\"+to_string(summary.confidence);\n    pnp__msg += string( \"   elapsed_dls_pnp_ransac (ms)=\")+to_string(elapsed_dls_pnp_ransac)+\";\";\n\n\n    c_T_w = best_rel_pose.b_T_a;\n    return summary.confidence;\n    #endif\n\n\n\n}\n\n#endif\n\n\n\n// #define __StaticCeresPoseCompute_PNP_(msg) msg;\n#define __StaticCeresPoseCompute_PNP_(msg) ;\nfloat StaticCeresPoseCompute::PNP( const std::vector<Vector3d>& w_X, const std::vector<Vector2d>& c_uv_normalized,\n    Matrix4d& c_T_w,\n    string& pnp__msg )\n{\n    assert( w_X.size() == c_uv_normalized.size() && w_X.size() > 8 );\n    if( w_X.size() != c_uv_normalized.size() ) {\n        cout << \"[StaticCeresPoseCompute::PNP] w_X.size() != c_uv_normalized.size()\\n\";\n        exit(  1 );\n    }\n    __StaticCeresPoseCompute_PNP_(\n    cout << \"w_X.size()=\" << w_X.size() << \"  c_uv_normalized.size()=\" << c_uv_normalized.size() << endl;\n    )\n\n    // setup ceres problem for PNP\n    //      minimize_{R,t} \\sum_i (  PI( c_(R|t)_w * w_X[i] ) - u[i] )\n    ceres::Problem problem;\n\n    // optimization variables\n    double ypr[3], tr[3];\n    PoseManipUtils::eigenmat_to_rawyprt( c_T_w, ypr, tr );\n    // double yaw=ypr[0], pitch=ypr[1], roll=ypr[2], tx=tr[0], ty=tr[1], tz=tr[2];\n\n    for( int i=0 ; i<w_X.size() ; i++ )\n    {\n        // cout << w_X[i].transpose() << \" <---> \" << c_uv_normalized[i].transpose() << endl;\n        ceres::CostFunction * cost_function = PNPEulerAngleError::Create( w_X[i], c_uv_normalized[i] );\n        problem.AddResidualBlock( cost_function, new ceres::HuberLoss(0.1), &ypr[0], &ypr[1], &ypr[2], &tr[0], &tr[1], &tr[2] );\n        // problem.AddResidualBlock( cost_function, NULL, &ypr[0], &ypr[1], &ypr[2], &tr[0], &tr[1], &tr[2] );\n\n    }\n\n\n    // Local Parameterization (Angle)\n    ceres::LocalParameterization *angle_parameterization = AngleLocalParameterization::Create();\n    problem.SetParameterization( &ypr[0], angle_parameterization );\n    problem.SetParameterization( &ypr[1], angle_parameterization );\n    problem.SetParameterization( &ypr[2], angle_parameterization );\n\n    // Set as constant\n    problem.SetParameterBlockConstant( &ypr[1] ); //pitch\n    problem.SetParameterBlockConstant( &ypr[2] ); //roll\n\n\n    ceres::Solver::Options options;\n    options.linear_solver_type = ceres::DENSE_SCHUR;\n    options.minimizer_progress_to_stdout = false;\n    // options.trust_region_strategy_type = ceres::DOGLEG;\n    // options.dogleg_type = ceres::SUBSPACE_DOGLEG;\n    ceres::Solver::Summary summary;\n    __StaticCeresPoseCompute_PNP_(\n    cout << \"yaw,pitch,roll=\" << ypr[0] << \" \" << ypr[1] << \" \" << ypr[2] << \"; tx,ty,tz=\" << tr[0] << \" \"<< tr[1] << \" \" << tr[2] << endl;\n    cout << \"Ceres::Solve()\\n\";\n    )\n\n    pnp__msg += \"initial_guess: \"+PoseManipUtils::prettyprintMatrix4d( c_T_w ) + \";\";\n    ceres::Solve(options, &problem, &summary);\n    __StaticCeresPoseCompute_PNP_(\n    cout << \"yaw,pitch,roll=\" << ypr[0] << \" \" << ypr[1] << \" \" << ypr[2] << \"; tx,ty,tz=\" << tr[0] << \" \"<< tr[1] << \" \" << tr[2] << endl;\n    )\n    // cout << summary.FullReport() << endl;\n    cout << summary.BriefReport() << endl;\n    pnp__msg += summary.BriefReport() + \";\";\n\n\n    PoseManipUtils::rawyprt_to_eigenmat( ypr, tr, c_T_w );\n    pnp__msg += \"final: \"+PoseManipUtils::prettyprintMatrix4d( c_T_w ) + \";\";\n\n    return 1.0;\n}\n\n\n\n\n\n\n#define __StaticCeresPoseCompute_P3P_(msg) msg;\n// #define __StaticCeresPoseCompute_P3P_(msg) ;\nfloat StaticCeresPoseCompute::P3P_ICP( const std::vector<Vector3d>& a_X, const std::vector<Vector3d>& b_X,\n    Matrix4d& b_T_a,\n    string& p3p__msg )\n{\n    assert( a_X.size() == b_X.size() && a_X.size() > 8 );\n    if( a_X.size() != b_X.size() ) {\n        cout << \"[StaticCeresPoseCompute::P3P] a_X.size() != b_X.size()\\n\";\n        exit(  1 );\n    }\n    __StaticCeresPoseCompute_P3P_(\n    cout << \"a_X.size()=\" << a_X.size() << \"  b_X.size()=\" << b_X.size() << endl;\n    )\n\n    // setup ceres problem for PNP\n    //      minimize_{R,t} \\sum_i (   c_(R|t)_w * a_X[i] - b_X[i]   )\n    ceres::Problem problem;\n\n    // optimization variables\n    double ypr[3], tr[3];\n    PoseManipUtils::eigenmat_to_rawyprt( b_T_a, ypr, tr );\n    // double yaw=ypr[0], pitch=ypr[1], roll=ypr[2], tx=tr[0], ty=tr[1], tz=tr[2];\n\n    for( int i=0 ; i<a_X.size() ; i++ )\n    {\n        // cout << a_X[i].transpose() << \" <---> \" << b_X[i].transpose() << endl;\n        ceres::CostFunction * cost_function = P3PEulerAngleError::Create( a_X[i], b_X[i] );\n        problem.AddResidualBlock( cost_function, new ceres::HuberLoss(0.1), &ypr[0], &ypr[1], &ypr[2], &tr[0], &tr[1], &tr[2] );\n        // problem.AddResidualBlock( cost_function, NULL, &ypr[0], &ypr[1], &ypr[2], &tr[0], &tr[1], &tr[2] );\n\n    }\n\n\n    // Local Parameterization (Angle)\n    ceres::LocalParameterization *angle_parameterization = AngleLocalParameterization::Create();\n    problem.SetParameterization( &ypr[0], angle_parameterization );\n    problem.SetParameterization( &ypr[1], angle_parameterization );\n    problem.SetParameterization( &ypr[2], angle_parameterization );\n\n    // Set as constant\n    problem.SetParameterBlockConstant( &ypr[1] ); //pitch\n    problem.SetParameterBlockConstant( &ypr[2] ); //roll\n\n\n    ceres::Solver::Options options;\n    options.linear_solver_type = ceres::DENSE_SCHUR;\n    options.minimizer_progress_to_stdout = false;\n    // options.trust_region_strategy_type = ceres::DOGLEG;\n    // options.dogleg_type = ceres::SUBSPACE_DOGLEG;\n    ceres::Solver::Summary summary;\n    __StaticCeresPoseCompute_P3P_(\n    cout << \"yaw,pitch,roll=\" << ypr[0] << \" \" << ypr[1] << \" \" << ypr[2] << \"; tx,ty,tz=\" << tr[0] << \" \"<< tr[1] << \" \" << tr[2] << endl;\n    cout << \"Ceres::Solve()\\n\";\n    )\n    p3p__msg += \"initial bTa: \"+PoseManipUtils::prettyprintMatrix4d( b_T_a ) + \";\";\n    ceres::Solve(options, &problem, &summary);\n    __StaticCeresPoseCompute_P3P_(\n    cout << \"yaw,pitch,roll=\" << ypr[0] << \" \" << ypr[1] << \" \" << ypr[2] << \"; tx,ty,tz=\" << tr[0] << \" \"<< tr[1] << \" \" << tr[2] << endl;\n    )\n    __StaticCeresPoseCompute_P3P_(\n    // cout << summary.FullReport() << endl;\n    cout << summary.BriefReport() << endl;\n    )\n    p3p__msg += summary.BriefReport() + \";\";\n\n    PoseManipUtils::rawyprt_to_eigenmat( ypr, tr, b_T_a );\n    p3p__msg += \"final: \"+PoseManipUtils::prettyprintMatrix4d( b_T_a ) + \";\";\n\n    return 1.0;\n}\n"
  },
  {
    "path": "src/DlsPnpWithRansac.h",
    "content": "#pragma once\n\n/* This is a RanSAC wrapper for theia::DlsPnp. Based on sample code on theia-sfm website.\n    The official website has some obivious issues.\n\n        Author  : Manohar Kuse <mpkuse@connect.ust.hk>\n        Created : 4th Jan, 2018\n*/\n\n\n#include \"utils/TermColor.h\"\n#include \"utils/ElapsedTime.h\"\n#include \"utils/PoseManipUtils.h\"\n\n#include <vector>\n\n#include <Eigen/Dense>\n#include <Eigen/Geometry>\n\n#include \"PNPCeresCostFunctions.h\"\n\nusing namespace Eigen;\nusing namespace std;\n\n\n#define __USE_THEIASFM //comment this out to compile with theia-sfm\n#ifdef __USE_THEIASFM\n#include <theia/theia.h>\n/////////// DlsPnp-RANSAC ///////////////////\n// Data\nstruct CorrespondencePair_3d2d {\n    Vector3d a_X; // 3d point expressed in co-ordinate system of `image-a`\n    Vector2d uv_d; //feature point in normalized-image co-ordinates. of the other view (b)\n};\n\n// Model\nstruct RelativePose {\n    Matrix4d b_T_a;\n};\n\n\nclass DlsPnpWithRansac: public theia::Estimator<CorrespondencePair_3d2d, RelativePose> {\npublic:\n    // Number of points needed to estimate a line.\n    double SampleSize() const  { return 15; }\n\n    // Estimate a pose from N points\n    bool EstimateModel( const std::vector<CorrespondencePair_3d2d>& data,\n                            std::vector<RelativePose>* models) const {\n\n        std::vector<Vector2d> feature_position;\n        std::vector<Vector3d> world_point;\n        for( int i=0 ; i<data.size() ; i++ ) {\n            feature_position.push_back( data[i].uv_d );\n            world_point.push_back( data[i].a_X );\n        }\n\n        std::vector<Quaterniond> solution_rotations;\n        std::vector<Vector3d> solution_translations;\n\n        theia::DlsPnp( feature_position, world_point, &solution_rotations, &solution_translations );\n        if( solution_rotations.size() == 1 ) {\n            RelativePose r;\n            r.b_T_a = Matrix4d::Identity();\n            r.b_T_a.topLeftCorner(3,3) = solution_rotations[0].toRotationMatrix();\n            r.b_T_a.col(3).topRows(3) = solution_translations[0];\n            models->push_back( r );\n        }\n        else {\n            return false;\n        }\n    }\n\n\n    double Error( const CorrespondencePair_3d2d& point, const RelativePose& r ) const {\n        Vector3d  b_X = r.b_T_a.topLeftCorner(3,3) * point.a_X + r.b_T_a.col(3).topRows(3);\n        double depth = b_X(2);\n        b_X /= b_X(2);\n\n        double reproj_error = abs(b_X(0) - point.uv_d(0)) + abs(b_X(1) - point.uv_d(1));\n\n        // I want nearby points to have smaller Error than far off points.\n        // So artificially increase the error when depth is higher for the point.\n        double f = 1.;\n        #if 0\n        if( depth < 1. )\n            f = .5;\n        if( depth >=1 && depth < 3 )\n            f = 5.0;\n        if( depth >=3 && depth < 8 )\n            f = 2.;\n        if( depth >= 8. && depth < 15 )\n            f = 1.7;\n        if( depth >= 15 )\n            f = 0.1;\n        #endif\n\n        return reproj_error*f;\n    }\n};\n/////////// END DlsPnp-RANSAC ///////////////////\n#endif\n\n#ifdef __USE_THEIASFM\n//////////////////// AlignPointCloudsUmeyama with Ransac ///////////////////////\n// Data\nstruct CorrespondencePair_3d3d {\n    Vector3d a_X; // 3d point expressed in co-ordinate system of `image-a`\n    Vector3d b_X; // 3d point expressed in co-ordinate system of `image-b`\n};\n\n// Model\n// struct RelativePose {\n    // Matrix4d b_T_a;\n// };\n\nclass AlignPointCloudsUmeyamaWithRansac: public theia::Estimator<CorrespondencePair_3d3d, RelativePose> {\npublic:\n    // Number of points needed to estimate a line.\n    double SampleSize() const  { return 10; }\n\n    bool EstimateModel( const std::vector<CorrespondencePair_3d3d>& data,\n                            std::vector<RelativePose>* models) const {\n        //TODO\n\n        std::vector<Vector3d> a_X;\n        std::vector<Vector3d> b_X;\n        for( int i=0 ; i<data.size() ; i++ ) {\n            a_X.push_back( data[i].a_X );\n            b_X.push_back( data[i].b_X );\n        }\n\n        Matrix3d ____R;\n        Vector3d ____t; double ___s=1.0;\n        theia::AlignPointCloudsUmeyama( a_X, b_X, &____R, &____t, &___s );\n\n        if( min( ___s, 1.0/___s ) > 0.9 ) {\n            RelativePose r;\n            r.b_T_a = Matrix4d::Identity();\n            r.b_T_a.topLeftCorner(3,3) = ____R;\n            r.b_T_a.col(3).topRows(3) = ____t;\n            models->push_back( r );\n            // cout << \"Estimate s=\" << ___s << \" \" << PoseManipUtils::prettyprintMatrix4d(r.b_T_a)<< endl;\n            return true;\n        } else {\n            return false;\n        }\n\n    }\n\n    double Error( const CorrespondencePair_3d3d& point, const RelativePose& r ) const {\n        Vector3d  b_X_cap = r.b_T_a.topLeftCorner(3,3) * point.a_X + r.b_T_a.col(3).topRows(3);\n\n        double err = (b_X_cap - point.b_X).norm();\n\n        double f=1.0;\n        if( point.a_X(2) < 1. && point.a_X(2) > 8. )\n            f = 1.2;\n\n        // cout << \"ICP RAnsac error=\" << err << endl;\n\n        return f*err;\n\n    }\n\n};\n\n\nclass StaticTheiaPoseCompute\n{\npublic:\n    static float P3P_ICP( const vector<Vector3d>& uv_X, const vector<Vector3d>& uvd_Y,\n        Matrix4d& uvd_T_uv, string & p3p__msg );\n\n    static float PNP( const std::vector<Vector3d>& w_X, const std::vector<Vector2d>& c_uv_normalized,\n        Matrix4d& c_T_w,\n        string& pnp__msg );\n\n};\n\n//////////////////// AlignPointCloudsUmeyama with Ransac ///////////////////////\n\n#endif\n\n// Write an ceres based iterative PnP and P3P for better control and 4DOF optimization\nclass StaticCeresPoseCompute\n{\npublic:\n    // PNP With ceres:     minimize_{R,t} \\sum_i (  PI( c_(R|t)_w * w_X[i] ) - u[i] )\n    // [Input]\n    //      w_X: World 3d points.\n    //      c_uv_normalized: Image points in normalized co-ordinates\n    //      c_T_w: initial guess, this will be modified to reflect the optimized solution\n    // [Output]\n    //      c_T_w\n    //      pnp__msg: debugging string msg\n    static float PNP( const std::vector<Vector3d>& w_X, const std::vector<Vector2d>& c_uv_normalized,\n        Matrix4d& c_T_w,\n        string& pnp__msg );\n\n\n    // p3p (icp) with ceres: minimize_{R,t} \\sum_i ( b_{R,t}_a * a_X - b_X )\n    // [Input]\n    //      a_X : 3d points in co-ordinate frame of a\n    //      b_X : 3d points in co-orfinate frame of b\n    //      b_T_a: initial guess\n    // [Output]\n    //      b_T_a : optimized output\n    //      p3p_msg: debug msg\n    static float P3P_ICP( const std::vector<Vector3d>& a_X, const std::vector<Vector3d>& b_X,\n        Matrix4d& c_T_w,\n        string& pnp__msg );\n\n};\n"
  },
  {
    "path": "src/HypothesisManager.cpp",
    "content": "#include \"HypothesisManager.h\"\n\n\nHypothesisManager::HypothesisManager()\n{\n    std::cout << \"HypothesisManager constructor\\n\";\n}\n\nHypothesisManager::~HypothesisManager()\n{\n    std::cout << \"HypothesisManager descrutctor\\n\";\n}\n\n\nbool HypothesisManager::add_node( int a, int b, float dot_prod )\n{\n    // See if (a,b) is in a already active hypothesis ?\n    //      |---(yes) give strength, possibly increase its time-to-live (ttl)\n    //      |---(no ) is it worth creating new hypothesis?\n    //              |--- (yes)\n    //              |--- (no) discard this\n    std::lock_guard<std::mutex> lk(mx);\n\n    cout << \"\\n[HypothesisManager::add_node] add_node(\"<<a <<\", \" << b << \", \" << dot_prod << \")\\n\";\n\n    //-----------a\n    bool was_in_one_of_the_active_hyp = false;\n    cout << \"[HypothesisManager::add_node] #active_hyp=\" << active_hyp.size() << endl;\n    for( auto it=active_hyp.begin() ; it!=active_hyp.end() ; it++ ) // loop over active hyp\n    {\n        if( was_in_one_of_the_active_hyp == true )\n            break;\n\n        #if 0\n        // cout << \"active_hyp#\" << it-active_hyp.begin() << endl;\n        auto header_str = string(\"\");\n        if( it == active_hyp.begin() )\n            header_str = \"\\n---\\n\";\n        header_str +=\"active_hyp#\" + std::to_string(  it-active_hyp.begin() );\n        it->print_active_hypothesis(header_str);\n        #endif\n\n        for( auto c=it->list_of_nodes_in_this_hypothesis.rbegin() ;\n                  c!=it->list_of_nodes_in_this_hypothesis.rend() ;\n                  c++    )\n        {\n            int _a = std::get<0>( *c );\n            int _b = std::get<1>( *c );\n            float _dot_prod = std::get<2>( *c );\n\n            if( abs(a-_a) < 7 && abs(b-_b) < 7 ) {\n                it->list_of_nodes_in_this_hypothesis.push_back( make_tuple(a,b,dot_prod) );\n                was_in_one_of_the_active_hyp = true;\n                printf( \"Added (%d,%d,%f) in active_hyp#%d\\n\", a,b,dot_prod, it-active_hyp.begin() );\n\n                printf( \"Increment time_to_live of this hypothesis\\n\" );\n                it->increment_ttl();\n                break;\n            }\n        }\n    }\n\n\n\n    //-------------b\n    if( was_in_one_of_the_active_hyp == false ) {\n        cout << \"New Hypothesis added: \"<< a<< \", \" << b << \", \" << dot_prod << endl;\n        active_hyp.push_back( Hypothesis( a,b, dot_prod) );\n    }\n\n\n}\n\nvoid HypothesisManager::digest()\n{\n    std::lock_guard<std::mutex> lk(mx);\n\n    for( auto it=active_hyp.begin() ; it!=active_hyp.end() ; it++ )\n    {\n        it->decrement_ttl();\n        it->decrement_ttl();\n        it->decrement_ttl();\n        it->decrement_ttl();\n        // it->decrement_ttl();\n    }\n}\n\n\nvoid HypothesisManager::monitoring_thread()\n{\n    cout << TermColor::GREEN() << \"[HypothesisManager::monitoring_thread] thread started\\n\" << TermColor::RESET() << endl;\n    while( b_monitoring_thread )\n    {\n        {   // threadsafe zone\n            std::lock_guard<std::mutex> lk(mx);\n            ofstream myfile;\n            myfile.open (\"/dev/pts/1\");\n            int n_actives = 0;\n            for( int i=0 ; i<active_hyp.size() ; i++ )\n            {\n                if( i==0 )\n                    myfile << \"---\\n\";\n\n                if( active_hyp.at(i).is_hypothesis_active() ) {\n                    n_actives++;\n                    myfile << \"i=\" << i << \"  \" << active_hyp.at(i).info_string() << endl;\n                }\n            }\n\n            if( n_actives > 0 ) {\n                myfile << TermColor::GREEN() << \"Currently active hypothesis=\" << n_actives <<  TermColor::RESET() << endl;\n            }\n            else {\n                myfile << TermColor::RED() << \"No active hypothesis\" << TermColor::RESET() << endl;\n            }\n            myfile.close();\n\n        } // end of threadsafe zone\n\n        std::this_thread::sleep_for(std::chrono::milliseconds(1000));\n\n    }\n\n    cout << TermColor::RED() << \"[HypothesisManager::monitoring_thread] thread Ended\\n\" << TermColor::RESET() << endl;\n}\n"
  },
  {
    "path": "src/HypothesisManager.h",
    "content": "#pragma once\n// This class will manage all the loop hypothesis.\n// This is for implementing the heuristics for multi hypothesis tracking.\n// A new graph-node is created when I have a new putative matching pair.\n// This matching pair is associated with an existing hypothesis or a new\n// hypothesis is created. Allthe hypothesis will have a time-to-live.\n\n#include <iostream>\n#include <fstream>\n#include <sstream>\n\n#include <string>\n#include <iomanip>\n#include <algorithm>\n#include <vector>\n#include <tuple>\n\n#include <thread>\n#include <mutex>\n#include <atomic>\n#include <chrono>\n\n#include \"utils/TermColor.h\"\nusing namespace std;\n\nclass Hypothesis\n{\npublic:\n    Hypothesis(int a, int b, float prod )\n    {\n        list_of_nodes_in_this_hypothesis.push_back( std::make_tuple(a,b, prod) );\n        time_to_live = 20;\n    }\n    std::vector< std::tuple<int,int, float> >  list_of_nodes_in_this_hypothesis;\n\n    string info_string()\n    {\n        std::stringstream ss;\n        ss << \"ttl=\" << time_to_live << \" \";\n        ss << \"\\tlen(list) = \" << list_of_nodes_in_this_hypothesis.size() << \"\\t\";\n\n        //starts and ends of the hyp\n        vector<int> tmp_a, tmp_b;\n        for( auto it=  list_of_nodes_in_this_hypothesis.begin() ;\n                  it!= list_of_nodes_in_this_hypothesis.end() ;\n                  it++ )\n        {\n            tmp_a.push_back( std::get<0>(*it) );\n            tmp_b.push_back( std::get<1>(*it) );\n        }\n\n        const auto result_a = std::minmax_element( tmp_a.begin(), tmp_a.end() );\n        const auto result_b = std::minmax_element( tmp_b.begin(), tmp_b.end() );\n\n        ss << *result_a.first << \"---->\" << *result_a.second  << \"  \";\n        ss << *result_b.first << \"---->\" << *result_b.second  << \"  \";\n\n        return ss.str();\n    }\n\n    #if 0\n    void print_active_hypothesis( const string header_str ) {\n\n        ofstream myfile;\n        myfile.open (\"/dev/pts/1\");\n\n        myfile << header_str << \"\\t\\t\";\n        myfile << \"ttl=\" << time_to_live << \" \";\n        myfile << \"\\tlen(list) = \" << list_of_nodes_in_this_hypothesis.size() << \"\\t\";\n        vector<int> tmp_a, tmp_b;\n        for( auto it=  list_of_nodes_in_this_hypothesis.begin() ;\n                  it!= list_of_nodes_in_this_hypothesis.end() ;\n                  it++ )\n        {\n            tmp_a.push_back( std::get<0>(*it) );\n            tmp_b.push_back( std::get<1>(*it) );\n        }\n\n        // cout <<\n        const auto result_a = std::minmax_element( tmp_a.begin(), tmp_a.end() );\n        const auto result_b = std::minmax_element( tmp_b.begin(), tmp_b.end() );\n\n        myfile << *result_a.first << \"---->\" << *result_a.second  << \"  \";\n        myfile << *result_b.first << \"---->\" << *result_b.second  << \"  \";\n        myfile << endl;\n\n        #if 0 //full details\n        for( auto it=  list_of_nodes_in_this_hypothesis.begin() ;\n                  it!= list_of_nodes_in_this_hypothesis.end() ;\n                  it++ )\n        {\n            myfile << \" (\" << std::get<0>(*it) << \",\" << std::get<1>(*it) << \") \";\n        }\n        myfile << endl;\n        #endif\n\n        myfile.close();\n    }\n    #endif\n\n\npublic:\n    void decrement_ttl()\n    {\n        if( time_to_live <= 0 )\n            return;\n        time_to_live--;\n    }\n\n    void increment_ttl()\n    {\n        // linear increment\n        time_to_live++;\n\n\n        if( time_to_live > 100 )\n            time_to_live++;\n\n        // multiplicative increment\n        // time_to_live += int(.1*time_to_live);\n\n    }\n    int get_ttl() { return time_to_live; }\n\n    bool is_hypothesis_active() { if(time_to_live<=0) return false; else return true; }\n    int n_elements_in_list() { return list_of_nodes_in_this_hypothesis.size(); }\n\nprivate:\n    int time_to_live;\n\n};\n\nclass HypothesisManager\n{\npublic:\n    HypothesisManager();\n    ~HypothesisManager();\n\n    // We add a node in the graph for every putative loop candidate.\n    // a,b:  Indicates a loop connection between node[a] and node[b] with a dot_product=dot_prod\n    bool add_node( int a, int b, float dot_prod );\n\n\n    //---\n    // Info functions\n    //---\n    // int n_active_hypothesis();\n\n\n    //---\n    // Loop through each active hypothesis and decrement the time-to-live\n    // This is intented to be called every few milisecs.\n    void digest();\n\n\n\nprivate:\n    mutable mutex mx;\n    std::vector<Hypothesis> active_hyp;\n\n\n//----------------------------\n//--- Monitoring Thread\n//----------------------------\npublic:\n    void monitoring_thread();\n    void monitoring_thread_enable() { b_monitoring_thread = true;}\n    void monitoring_thread_disable() {b_monitoring_thread = false;}\n\nprivate:\n    atomic<bool> b_monitoring_thread;\n\n};\n"
  },
  {
    "path": "src/ImageDataManager.cpp",
    "content": "#include \"ImageDataManager.h\"\n\n\nImageDataManager::ImageDataManager()\n{\n    cout << TermColor::iYELLOW() << \"Constructor for ImageDataManager\\n\" << TermColor::RESET();\n\n}\n\nbool ImageDataManager::initStashDir( bool clear_dir, const string __dir )\n{\n    STASH_DIR = __dir; //string( \"/tmp/cerebro_stash\" );\n\n    if( clear_dir ) // rm -rf && mksir\n    {\n        //\n        // $ rm -rf ${save_dir}\n        // $ mkdir ${save_dir}\n        // #include <cstdlib>\n        string system_cmd;\n\n        system_cmd = string(\"rm -rf \"+STASH_DIR).c_str();\n        cout << TermColor::YELLOW() << \"[ImageDataManager::initStashDir] \" << system_cmd << TermColor::RESET() << endl;\n        const int rm_dir_err = RawFileIO::exec_cmd( system_cmd );\n\n        if ( rm_dir_err == -1 )\n        {\n            cout << TermColor::RED() << \"[ImageDataManager::initStashDir] Error removing directory!\\n\" << TermColor::RESET() << endl;\n            exit(1);\n        }\n\n        system_cmd = string(\"mkdir -p \"+STASH_DIR).c_str();\n        cout << TermColor::YELLOW() << \"[ImageDataManager::initStashDir] \" << system_cmd << TermColor::RESET() << endl;\n        // const int dir_err = system( system_cmd.c_str() );\n        const int dir_err = RawFileIO::exec_cmd( system_cmd );\n        if ( dir_err == -1 )\n        {\n            cout << TermColor::RED() << \"[ImageDataManager::initStashDir] Error creating directory!\\n\" << TermColor::RESET() << endl;\n            exit(1);\n        }\n        // done emptying the directory.\n    } else {\n        // make sure the path is a directory\n        if( RawFileIO::is_path_a_directory(STASH_DIR) ) {\n            // ok good!\n        } else {\n            cout << TermColor::RED() << \"[ImageDataManager::initStashDir]You asked me to assume the directory ``\"<< STASH_DIR << \"`` as stash directory, but it doesnt seem to exist...EXIT\" << TermColor::RESET() << endl;\n            ROS_ERROR( \"[ImageDataManager::initStashDir]You asked me to assume the directory ``%s`` as stash directory, but it doesnt seem to exist...EXIT\", STASH_DIR.c_str() );\n            exit(1);\n        }\n    }\n\n    m_STASH_DIR = true;\n}\n\nImageDataManager::~ImageDataManager()\n{\n    std::lock_guard<std::mutex> lk(m);\n    // TODO\n    // rm all available on RAM\n    status.clear();\n    image_data.clear();\n\n    if( rm_stash_dir_in_destructor )\n    {\n        // rm -rf stash folder\n        string system_cmd;\n\n        system_cmd = string(\"rm -rf \"+STASH_DIR).c_str();\n        cout << TermColor::YELLOW() << \"[ImageDataManager::~ImageDataManager] \" << system_cmd << TermColor::RESET() << endl;\n        const int rm_dir_err = RawFileIO::exec_cmd( system_cmd );\n\n        if ( rm_dir_err == -1 )\n        {\n            cout << TermColor::RED() << \"[ImageDataManager::~ImageDataManager] Error removing directory!\\n\" << TermColor::RESET() << endl;\n            exit(1);\n        }\n    }\n    else {\n        cout << TermColor::YELLOW() << \"[ImageDataManager::~ImageDataManager] not doing rm -rf \" << STASH_DIR << \", because the bool variable was found to be false\\n\";\n    }\n\n}\n\nbool ImageDataManager::setImage( const string ns, const ros::Time t, const cv::Mat img )\n{\n    cout << \"[ImageDataManager::setImage] not implemented\\n\";\n    exit(1);\n}\n\n// #define __ImageDataManager__setImageFromMsg( msg ) msg;\n#define __ImageDataManager__setImageFromMsg( msg ) ;\nbool ImageDataManager::setNewImageFromMsg( const string ns, const sensor_msgs::ImageConstPtr msg )\n{\n    std::lock_guard<std::mutex> lk(m);\n    cv::Mat __image = (cv_bridge::toCvCopy(msg)->image).clone();\n\n\n    auto key = std::make_pair(ns, msg->header.stamp);\n    __ImageDataManager__setImageFromMsg(\n    cout << TermColor::iWHITE() << \"[ImageDataManager::setImageFromMsg] insert at(\" << ns << \",\" << msg->header.stamp << \")\" << TermColor::RESET() << endl;\n    )\n\n    // TODO: throw error if attempting to over-write\n    status[ key ] = MEMSTAT::AVAILABLE_ON_RAM;\n    image_data[ key ] = __image;\n    return true;\n}\n\n\n// #define __ImageDataManager__getImage( msg ) msg;\n#define __ImageDataManager__getImage( msg ) ;\nbool ImageDataManager::getImage( const string ns, const ros::Time t, cv::Mat& outImg )\n{\n    ensure_init();\n    decrement_hit_counts_and_deallocate_expired();\n\n    std::lock_guard<std::mutex> lk(m);\n\n    auto key = std::make_pair(ns,t);\n    if( status.count(key) > 0 ) {\n        if( status.at(key) == MEMSTAT::AVAILABLE_ON_RAM ) {\n            __ImageDataManager__getImage(\n            cout << TermColor::iGREEN() << \"[ImageDataManager::getImage] retrived (from ram) at ns=\" << ns << \" t=\" << t << TermColor::RESET() << endl;\n            )\n            outImg = image_data.at( key );\n            return true;\n        }\n        else {\n            if( status.at(key) == MEMSTAT::AVAILABLE_ON_DISK )\n            {\n                const string fname = key_to_imagename(ns, t);\n                __ImageDataManager__getImage(\n                cout << TermColor::iYELLOW() << \"[ImageDataManager::getImage] retrived (fname=\" << fname << \") at ns=\" << ns << \" t=\" << t << TermColor::RESET() << endl;\n                )\n\n                if( ns == \"depth_image\" ) {\n                    outImg = cv::imread( fname+\".png\", -1 );\n                } else {\n                    outImg = cv::imread( fname, -1 ); //-1 is for read as it is.\n                }\n                if( !outImg.data )\n                {\n                    cout << TermColor::RED() << \"[ImageDataManager::getImage] failed to load image \" << fname << TermColor::RESET() << endl;\n                    exit(1);\n                    return false;\n                }\n\n                //=----------------cache hit this\n                __ImageDataManager__getImage(\n                cout << TermColor::iYELLOW() << \"\\t\\tchange the status to MEMSTAT::AVAILABLE_ON_RAM_DUETO_HIT and set expiry to 10\\n\" << TermColor::RESET();\n                )\n                status[key] = MEMSTAT::AVAILABLE_ON_RAM_DUETO_HIT;\n                image_data[key] = outImg;\n                hit_count[key] = 10;\n                //=----------------\n                return true;\n            }\n\n            if( status.at(key) == MEMSTAT::AVAILABLE_ON_RAM_DUETO_HIT )\n            {\n                __ImageDataManager__getImage(\n                cout << TermColor::iBLUE() << \"[ImageDataManager::getImage]  retrived (AVAILABLE_ON_RAM_DUETO_HIT) at ns=\" << ns << \" t=\" << t << TermColor::RESET()  << endl;\n                )\n                outImg = image_data.at(key);\n                hit_count[key] +=5;\n                return true;\n            }\n\n            if( status.at(key) == MEMSTAT::UNAVAILABLE )\n            {\n                cout << TermColor::iYELLOW() << \"[ImageDataManager::getImage] WARNING you requested image which was MEMSTAT::UNAVAILABLE, this should not be happening but for now return false,  at ns=\" << ns << \" t=\" << t << TermColor::RESET() << endl;\n                return false;\n            }\n\n\n            cout <<  TermColor::iGREEN() <<  \"[ImageDataManager::getImage] got a corrupt key. Report this error to authors if this occurs\\n\" << TermColor::RESET();\n            exit(1);\n        }\n    }\n    else\n    {\n        cout << TermColor::RED() << \"[ImageDataManager::getImage] FATAL-ERROR you requested image ns=\" << ns << \", t=\" << t << \"; However it was not found on the map. FATAL ERRR\\n\" << TermColor::RESET();\n        exit(1);\n        return false;\n    }\n\n    return false;\n}\n\n\n// #define __ImageDataManager__rmImage(msg) msg;\n#define __ImageDataManager__rmImage(msg) ;\nbool ImageDataManager::rmImage( const string ns, const ros::Time t )\n{\n    ensure_init();\n    std::lock_guard<std::mutex> lk(m);\n    auto key = std::make_pair(ns, t);\n    if( status.count(key) > 0  )\n    {\n        if( status.at(key) == MEMSTAT::AVAILABLE_ON_RAM ) {\n            __ImageDataManager__rmImage(\n            cout << TermColor::iWHITE() << \"[ImageDataManager::rmImage] ns=\" << ns << \", t=\" << t << \". FOUND, available on ram, do complete removal\" << TermColor::RESET() << endl;\n            )\n            status[key] = MEMSTAT::UNAVAILABLE;\n\n            auto it_b = image_data.find( key );\n            image_data.erase( it_b );\n            return true;\n        }\n        else {\n            __ImageDataManager__rmImage(\n            cout << TermColor::iWHITE() << \"[ImageDataManager::rmImage] ns=\" << ns << \", t=\" << t << \".\";\n            cout << \"FOUND, however its status is UNAVAILABLE or AVAILABLE_ON_DISK, this means it was previously removed or stashed. No action to take now.\" << TermColor::RESET() << endl;\n            )\n            return false;\n        }\n\n    }\n    else\n    {\n        cout << TermColor::RED() << \"[ImageDataManager::rmImage] FATAL-ERROR you requested to remove ns=\" << ns << \", t=\" << t << \"; However it was not found on the map. FATAL ERRR\\n\" << TermColor::RESET();\n        // exit(1);\n        cout << \"[ImageDataManager::rmImage]No action taken for now.....\\n\";\n        return false;\n    }\n\n}\n\n// #define __ImageDataManager__stashImage(msg) msg;\n#define __ImageDataManager__stashImage(msg) ;\nbool ImageDataManager::stashImage( const string ns, const ros::Time t )\n{\n    ensure_init();\n    std::lock_guard<std::mutex> lk(m);\n    __ImageDataManager__stashImage(\n    cout << TermColor::iCYAN() << \"[ImageDataManager::stashImage] ns=\" << ns << \", t=\" << t << TermColor::RESET() << endl;\n    )\n    auto key = std::make_pair( ns, t );\n    if( status.count( key ) > 0 )\n    {\n        if( status.at(key) == MEMSTAT::AVAILABLE_ON_RAM )\n        {\n            // save to disk\n            auto it_b = image_data.find( key );\n            // string sfname = STASH_DIR+\"/\"+ns+\"__\"+to_string(t.toNSec())+\".jpg\";\n            string sfname = key_to_imagename( ns, t );\n            ElapsedTime elp; elp.tic();\n            __ImageDataManager__stashImage(\n            cout << TermColor::iCYAN() << \"[ImageDataManager] imwrite(\" << sfname  << \")\\t elapsed_time_ms=\" << elp.toc_milli() << TermColor::RESET() << endl ;\n            )\n\n            // todo: tune jpg quality for saving on more disk space. default is 95/100 for jpg.\n            if( it_b->second.type() == CV_16UC1 ) {\n                // write PNG for depth image\n                cv::imwrite( sfname+\".png\", it_b->second );\n            }else {\n                cv::imwrite( sfname, it_b->second );\n            }\n\n            // erase from map\n            image_data.erase( it_b );\n\n            // change status\n            status[key] = MEMSTAT::AVAILABLE_ON_DISK;\n            return true;\n        } else {\n            __ImageDataManager__stashImage(\n            cout << TermColor::iCYAN() << \"[ImageDataManager::stashImage] warning status is not AVAILABLE_ON_RAM so do nothing. You asked me to stash an image which doesnt exists on RAM.\\n\" << TermColor::RESET();\n            )\n            return false;\n        }\n    }\n    else\n    {\n        cout << TermColor::RED() << \"[ImageDataManager::stashImage] FATAL-ERROR you requested to stash ns=\" << ns << \", t=\" << t << \"; However it was not found on the map. FATAL ERRR\\n\" << TermColor::RESET();\n        exit(1);\n    }\n}\n\nbool ImageDataManager::isImageRetrivable( const string ns, const ros::Time t ) const\n{\n    ensure_init();\n    std::lock_guard<std::mutex> lk(m);\n    auto key = std::make_pair( ns, t );\n    if( status.count( key ) > 0 ) {\n        if(\n            status.at( key ) == MEMSTAT::AVAILABLE_ON_RAM\n                ||\n            status.at( key ) == MEMSTAT::AVAILABLE_ON_DISK\n                ||\n            status.at( key ) == MEMSTAT::AVAILABLE_ON_RAM_DUETO_HIT\n          )\n        {\n            return true;\n        }\n        else {\n            cout << TermColor::iRED() << \"[ImageDataManager::isImageRetrivable] returning false because the requested (ns=\" << ns << \", t=\" << t << \") was on my list, but type was \"<< memstat_to_str( status.at( key ) ) << \", which is something other than AVAILABLE_ON_RAM or AVAILABLE_ON_DISK or AVAILABLE_ON_RAM_DUETO_HIT\" << TermColor::RESET() << endl;\n            return false;\n        }\n    }\n    else {\n        cout << TermColor::iRED() << \"[ImageDataManager::isImageRetrivable] returning false because the requested (ns=\" << ns << \", t=\" << t << \") was not in my list\" << TermColor::RESET() << endl;\n        return false;\n    }\n}\n\n// _ImageDataManager_printstatus_cout cout\n#define _ImageDataManager_printstatus_cout myfile\nbool ImageDataManager::print_status( string fname ) const\n{\n    // std::lock_guard<std::mutex> lk(m); //no need of locks in a const function (readonly).\n\n    ofstream myfile;\n    myfile.open (fname);\n\n    int AVAILABLE_ON_RAM=0, AVAILABLE_ON_DISK=0, UNAVAILABLE=0, ETC=0;\n    for( auto it=status.begin() ; it!=status.end() ; it++ )\n    {\n        if( it->second == MEMSTAT::AVAILABLE_ON_RAM ) {\n            _ImageDataManager_printstatus_cout << TermColor::iGREEN() << \" \" << TermColor::RESET();\n            AVAILABLE_ON_RAM++;\n        }\n        else\n        if( it->second == MEMSTAT::AVAILABLE_ON_DISK ) {\n            _ImageDataManager_printstatus_cout << TermColor::iYELLOW() << \" \" << TermColor::RESET();\n            AVAILABLE_ON_DISK++;\n        }\n        else\n        if( it->second == MEMSTAT::UNAVAILABLE ) {\n            _ImageDataManager_printstatus_cout << TermColor::iMAGENTA() << \" \" << TermColor::RESET();\n            UNAVAILABLE++;\n        }\n        else {\n        if( it->second == MEMSTAT::AVAILABLE_ON_RAM_DUETO_HIT )\n           _ImageDataManager_printstatus_cout << TermColor::iBLUE() << std::setw(2) << hit_count.at(it->first) << TermColor::RESET();\n           ETC++;\n       }\n\n\n    }\n    _ImageDataManager_printstatus_cout << endl;\n    _ImageDataManager_printstatus_cout <<  TermColor::iGREEN() << \"AVAILABLE_ON_RAM=\" << AVAILABLE_ON_RAM << \" \" << TermColor::RESET() ;\n    _ImageDataManager_printstatus_cout << TermColor::iYELLOW() << \"AVAILABLE_ON_DISK=\" << AVAILABLE_ON_DISK << \" \" << TermColor::RESET();\n    _ImageDataManager_printstatus_cout << TermColor::iMAGENTA() << \"UNAVAILABLE=\" << UNAVAILABLE << \" \" << TermColor::RESET();\n    _ImageDataManager_printstatus_cout << TermColor::iBLUE() << \"ETC=\" << ETC << \" \" << TermColor::RESET();\n    _ImageDataManager_printstatus_cout << \"TOTAL=\" << AVAILABLE_ON_RAM+AVAILABLE_ON_DISK+UNAVAILABLE+ETC << \" \";\n    _ImageDataManager_printstatus_cout << endl;\n\n    myfile.close();\n    return true;\n}\n\nbool ImageDataManager::print_status(  ) const\n{\n    // std::lock_guard<std::mutex> lk(m); //no need of locks in a const function (readonly).\n\n    int AVAILABLE_ON_RAM=0, AVAILABLE_ON_DISK=0, UNAVAILABLE=0, ETC=0;\n    for( auto it=status.begin() ; it!=status.end() ; it++ )\n    {\n        if( it->second == MEMSTAT::AVAILABLE_ON_RAM ) {\n            cout << TermColor::iGREEN() << \" \" << TermColor::RESET();\n            cout << std::get<0>(it->first) << \",\" << std::get<1>(it->first) << \": \" << \"AVAILABLE_ON_RAM\" << endl;\n            AVAILABLE_ON_RAM++;\n        }\n        else\n        if( it->second == MEMSTAT::AVAILABLE_ON_DISK ) {\n            cout << TermColor::iYELLOW() << \" \" << TermColor::RESET();\n            // cout << std::get<0>(it->first) << \",\" << std::get<1>(it->first) << \": \" << \"AVAILABLE_ON_DISK\" << endl;\n            AVAILABLE_ON_DISK++;\n        }\n        else\n        if( it->second == MEMSTAT::UNAVAILABLE ) {\n            cout << TermColor::iMAGENTA() << \" \" << TermColor::RESET();\n            // cout << std::get<0>(it->first) << \",\" << std::get<1>(it->first) << \": \" << \"UNAVAILABLE\" << endl;\n            UNAVAILABLE++;\n        }\n        else {\n        if( it->second == MEMSTAT::AVAILABLE_ON_RAM_DUETO_HIT )\n           cout << TermColor::iBLUE() << std::setw(2) << hit_count.at(it->first) << TermColor::RESET();\n        //    cout << std::get<0>(it->first) << \",\" << std::get<1>(it->first) << \": \" << \"AVAILABLE_ON_RAM_DUETO_HIT\" << endl;\n           ETC++;\n       }\n\n\n    }\n    cout << endl;\n    cout <<  TermColor::iGREEN() << \"AVAILABLE_ON_RAM=\" << AVAILABLE_ON_RAM << \" \" << TermColor::RESET() ;\n    cout << TermColor::iYELLOW() << \"AVAILABLE_ON_DISK=\" << AVAILABLE_ON_DISK << \" \" << TermColor::RESET();\n    cout << TermColor::iMAGENTA() << \"UNAVAILABLE=\" << UNAVAILABLE << \" \" << TermColor::RESET();\n    cout << TermColor::iBLUE() << \"ETC=\" << ETC << \" \" << TermColor::RESET();\n    cout << \"TOTAL=\" << AVAILABLE_ON_RAM+AVAILABLE_ON_DISK+UNAVAILABLE+ETC << \" \";\n    cout << endl;\n\n\n    return true;\n}\n\n\n// #define __ImageDataManager__decrement_hit_counts_and_deallocate_expired(msg) msg;\n#define __ImageDataManager__decrement_hit_counts_and_deallocate_expired(msg) ;\nint ImageDataManager::decrement_hit_counts_and_deallocate_expired()\n{\n    std::lock_guard<std::mutex> lk(m);\n\n    // decrement all by 1\n    int n_elements_in_hitlist = 0;\n    for( auto it=hit_count.begin() ; it!=hit_count.end() ; it++ )\n    {\n        it->second--;\n        __ImageDataManager__decrement_hit_counts_and_deallocate_expired(\n        cout << \"[decrement_hit_counts_and_deallocate_expired]\" << std::get<0>(it->first) << \",\" << std::get<1>(it->first) << \" expry=\" << it->second << endl;\n        )\n        n_elements_in_hitlist++;\n    }\n\n    // deallocate expired\n    int n_removed = 0;\n    for( auto it=hit_count.begin() ; it!=hit_count.end() ; it++ )\n    {\n        if( it->second <= 0 )\n        {\n            __ImageDataManager__decrement_hit_counts_and_deallocate_expired(\n            cout << \"[decrement_hit_counts_and_deallocate_expired] REMOVE: \" <<  std::get<0>(it->first) << \",\" << std::get<1>(it->first) << endl;\n            )\n            auto key = it->first;\n            image_data.erase(key);\n            hit_count.erase(key);\n            status[key] = MEMSTAT::AVAILABLE_ON_DISK;\n            n_removed++;\n        }\n    }\n\n    __ImageDataManager__decrement_hit_counts_and_deallocate_expired(\n    cout << \"[decrement_hit_counts_and_deallocate_expired] n_elements_in_hitlist=\" << n_elements_in_hitlist << \"\\tn_removed=\" << n_removed<< endl;\n    )\n    return n_removed;\n\n}\n\n\njson ImageDataManager::stashAll()\n{\n    ensure_init();\n    // std::lock_guard<std::mutex> lk(m); //dont lock here as stashImage already locks. If you lock here it will cause a deadlock.\n    json obj;\n\n    // go over all and stash all\n    int AVAILABLE_ON_RAM = 0;\n    int AVAILABLE_ON_DISK = 0;\n    int UNAVAILABLE = 0;\n    int AVAILABLE_ON_RAM_DUETO_HIT=0;\n    int n_stashed = 0;\n    for( auto it=status.begin() ; it!=status.end() ; it++ )\n    {\n        string __ns_x = std::get<0>(it->first);\n        ros::Time __t_x =  std::get<1>(it->first);\n\n        if( it->second == MEMSTAT::AVAILABLE_ON_RAM ) {\n            // cout << \"[ImageDataManager::stashAll] stash( \" << __ns_x << \", \" << __t_x << \" )\\n\";\n            stashImage( __ns_x, __t_x );\n            n_stashed++;\n        }\n\n        json this_json;\n        this_json[\"namespace\"] = __ns_x;\n        // this_json[\"stamp\"] = __t_x.toSec(); // this is buggy, have overflow, better stick to using Nsec int64_t\n        this_json[\"stampNSec\"] = __t_x.toNSec();\n\n        if( it->second == MEMSTAT::AVAILABLE_ON_RAM ) {\n            this_json[\"status\"] = \"AVAILABLE_ON_RAM\";\n            AVAILABLE_ON_RAM++;\n        }\n        else\n        if( it->second == MEMSTAT::AVAILABLE_ON_DISK ) {\n            this_json[\"status\"] = \"AVAILABLE_ON_DISK\";\n            AVAILABLE_ON_DISK++;\n        }\n        else\n        if( it->second == MEMSTAT::UNAVAILABLE ) {\n            this_json[\"status\"] = \"UNAVAILABLE\";\n            UNAVAILABLE++;\n        }\n        else {\n        if( it->second == MEMSTAT::AVAILABLE_ON_RAM_DUETO_HIT )\n            this_json[\"status\"] = \"AVAILABLE_ON_RAM_DUETO_HIT\";\n            AVAILABLE_ON_RAM_DUETO_HIT++;\n        }\n        obj.push_back( this_json );\n\n    }\n\n    cout << \"[ImageDataManager::stashAll]\";\n    cout << \"AVAILABLE_ON_RAM=\" << AVAILABLE_ON_RAM << \"\\t\";\n    cout << \"AVAILABLE_ON_DISK=\" << AVAILABLE_ON_DISK << \"\\t\";\n    cout << \"UNAVAILABLE=\" << UNAVAILABLE << \"\\t\";\n    cout << \"AVAILABLE_ON_RAM_DUETO_HIT=\" << AVAILABLE_ON_RAM_DUETO_HIT << \"\\t\";\n    cout << \"n_stashed=\" << n_stashed << \"\\t\";\n    cout << endl;\n    return obj;\n\n\n\n}\n\n\n\n//\n// \"ImageDataManager\": [\n//     {\n//          \"namespace\": \"left_image\",\n//          \"stamp\": 1542707155.6136055,\n//          \"status\": \"AVAILABLE_ON_DISK\"\n//      },\n//      {\n//          .\n//      },\n//      .\n//      .\n//      .\n//    ]\nbool ImageDataManager::loadStateFromDisk( const json json_obj )\n{\n    cout << \"^^^^^^^^ IN [ImageDataManager::loadStateFromDisk] ^^^^^^^^^^\\n\";\n    ensure_init();\n    // loop thru json object and create the map\n    int n_images = json_obj.size();\n    cout << \"[ImageDataManager::loadStateFromDisk] n_images=\" << n_images << endl;\n\n    int AVAILABLE_ON_DISK = 0;\n    int UNAVAILABLE = 0;\n    for( int i=0 ; i<n_images ; i++ )\n    {\n        string __ns_x = json_obj[i][\"namespace\"];\n        ros::Time __t_x = ros::Time().fromNSec( json_obj[i][\"stampNSec\"] );\n        string __status = json_obj[i][\"status\"];\n\n\n\n        auto key = std::make_pair(__ns_x, __t_x );\n        if( __status.compare(\"AVAILABLE_ON_DISK\") == 0 ) {\n            status[key] = MEMSTAT::AVAILABLE_ON_DISK;\n            AVAILABLE_ON_DISK++;\n        }\n        else if( __status.compare(\"UNAVAILABLE\") == 0 ){\n            status[key] = MEMSTAT::UNAVAILABLE;\n            UNAVAILABLE++;\n        } else if( __status.compare(\"AVAILABLE_ON_RAM_DUETO_HIT\") == 0  ){\n            status[key] = MEMSTAT::AVAILABLE_ON_DISK;\n            AVAILABLE_ON_DISK++;\n        }\n        else {\n            cout << \"[ImageDataManager::loadStateFromDisk] status in json can only be AVAILABLE_ON_DISK or UNAVAILABLE or AVAILABLE_ON_RAM_DUETO_HIT. If your json it is something other than these three. In case of AVAILABLE_ON_RAM_DUETO_HIT we set it as AVAILABLE_ON_DISK\\n\";\n            exit(1);\n        }\n    }\n\n    cout << \"[ImageDataManager::loadStateFromDisk]\";\n    cout << \"AVAILABLE_ON_DISK=\" << AVAILABLE_ON_DISK << \"\\t\";\n    cout << \"UNAVAILABLE=\" << UNAVAILABLE << \"\\t\";\n    cout << endl;\n\n\n    cout << \"^^^^^^^^ DONE [ImageDataManager::loadStateFromDisk] ^^^^^^^^^^\\n\";\n    return true;\n}\n"
  },
  {
    "path": "src/ImageDataManager.h",
    "content": "#pragma once\n\n// Getting lot of deallocation issues when storing images inside of DataNode.\n// This class now stores the image data and useless images are stored to\n// file to conserve ram.\n//\n//      Author  : Manohar Kuse <mpkuse@connect.ust.hk>\n//      Created : 12th June, 2019\n//\n\n#include <iostream>\n#include <string>\n#include <vector>\n#include <map>\n#include <utility> //std::pair\n#include <fstream>\n\n// threading\n#include <thread>\n#include <mutex>\n#include <atomic>\n\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <cv_bridge/cv_bridge.h>\n\n#include <ros/ros.h>\n\n#include \"utils/TermColor.h\"\n#include \"utils/ElapsedTime.h\"\n#include \"utils/RawFileIO.h\"\n\n\n#include \"utils/nlohmann/json.hpp\"\nusing json = nlohmann::json;\n\nusing namespace std;\n\nenum MEMSTAT { AVAILABLE_ON_RAM, AVAILABLE_ON_DISK, UNAVAILABLE, AVAILABLE_ON_RAM_DUETO_HIT };\n\nclass ImageDataManager\n{\npublic:\n    ImageDataManager();\n    bool initStashDir( bool clear_dir, const string __dir=string(\"/tmp/cerebro_stash/\")  ); //< clear_dir: setting this to true will cause `rm -rf DIR && mkdir DIR` ;;;__dir: The directory usually set it to '/tmp/cerebro_stash' .\n\n    ~ImageDataManager();\n    bool setImage( const string ns, const ros::Time t, const cv::Mat img );\n    bool setNewImageFromMsg( const string ns, const sensor_msgs::ImageConstPtr msg );\n    bool getImage( const string ns, const ros::Time t, cv::Mat& outImg );\n\n    bool rmImage( const string ns, const ros::Time t );\n    bool stashImage( const string ns, const ros::Time t );\n\n    bool isImageRetrivable( const string ns, const ros::Time t ) const;\n\n    bool print_status( string fname ) const;\n    bool print_status(  ) const;\n\n    // - go over all status and stash all the images that remain on RAM.\n    // Also retuns the map status as json object\n    json stashAll();\n    bool loadStateFromDisk( const json json_obj );\n\n\nprivate:\n    mutable std::mutex m;\n    string STASH_DIR = string(\"\"); bool m_STASH_DIR = false;\n    const string key_to_imagename( const string ns, const ros::Time t ) const\n    {\n        return STASH_DIR+\"/\"+ns+\"__\"+to_string(t.toNSec())+\".jpg\";\n    }\n\n    const string memstat_to_str( MEMSTAT m ) const\n    {\n        if( m == MEMSTAT::AVAILABLE_ON_RAM )\n            return \"AVAILABLE_ON_RAM\";\n\n        if( m == MEMSTAT::AVAILABLE_ON_DISK )\n            return \"AVAILABLE_ON_DISK\";\n\n        if( m == MEMSTAT::UNAVAILABLE )\n            return \"UNAVAILABLE\";\n\n        if( m == MEMSTAT::AVAILABLE_ON_RAM_DUETO_HIT )\n            return \"AVAILABLE_ON_RAM_DUETO_HIT\";\n\n        return \"N.A.\";\n    }\n\n    // key: (namespace, t)\n    std::map< std::pair<string , ros::Time>, MEMSTAT > status; //status at each timestamp (entris not be esared)\n    std::map< std::pair<string , ros::Time>, cv::Mat > image_data;\n\n\n    // when getImage finds that AVAILABLE_ON_DISK, it loads the image (from disk) in the map image_data.\n    // Also it stores say 10 in this map. This 10 means that the image be deleted again if 10 consecutive\n    // getImage requests dont request this image.\n    // CACHE-Algorithm: 5-minute-rule.\n    std::map<  std::pair<string , ros::Time>, int > hit_count;\n    int decrement_hit_counts_and_deallocate_expired(); //returns how many expired\n\n\n    bool rm_stash_dir_in_destructor = true;\n\npublic:\n    const string getStashDir() const { return STASH_DIR; }\n    void set_rm_stash_dir_in_destructor_as_false() {rm_stash_dir_in_destructor=false; }\n    void set_rm_stash_dir_in_destructor_as_true()  {rm_stash_dir_in_destructor=true; }\n\n    void ensure_init() const {\n        if( !m_STASH_DIR) {\n            ROS_ERROR( \"m_STASH_DIR is false. This means you have not initialized the ImageDataManager. You need to call the function initStashDir() befre you can start using it\\n\");\n            exit(1);\n        }\n    }\n};\n"
  },
  {
    "path": "src/PNPCeresCostFunctions.h",
    "content": "#pragma once\n\n//ceres\n#include <ceres/ceres.h>\n\ntemplate <typename T>\nT NormalizeAngle(const T& angle_degrees) {\n  if (angle_degrees > T(180.0))\n  \treturn angle_degrees - T(360.0);\n  else if (angle_degrees < T(-180.0))\n  \treturn angle_degrees + T(360.0);\n  else\n  \treturn angle_degrees;\n};\n\nclass AngleLocalParameterization {\n public:\n\n  template <typename T>\n  bool operator()(const T* theta_radians, const T* delta_theta_radians,\n                  T* theta_radians_plus_delta) const {\n    *theta_radians_plus_delta =\n        NormalizeAngle(*theta_radians + *delta_theta_radians);\n\n    return true;\n  }\n\n  static ceres::LocalParameterization* Create() {\n    return (new ceres::AutoDiffLocalParameterization<AngleLocalParameterization,\n                                                     1, 1>);\n  }\n};\n\ntemplate <typename T>\nvoid YawPitchRollToRotationMatrix(const T yaw, const T pitch, const T roll, T R[9])\n{\n\n\tT y = yaw / T(180.0) * T(M_PI);\n\tT p = pitch / T(180.0) * T(M_PI);\n\tT r = roll / T(180.0) * T(M_PI);\n\n\n\tR[0] = cos(y) * cos(p);\n\tR[1] = -sin(y) * cos(r) + cos(y) * sin(p) * sin(r);\n\tR[2] = sin(y) * sin(r) + cos(y) * sin(p) * cos(r);\n\tR[3] = sin(y) * cos(p);\n\tR[4] = cos(y) * cos(r) + sin(y) * sin(p) * sin(r);\n\tR[5] = -cos(y) * sin(r) + sin(y) * sin(p) * cos(r);\n\tR[6] = -sin(p);\n\tR[7] = cos(p) * sin(r);\n\tR[8] = cos(p) * cos(r);\n};\n\ntemplate <typename T>\nvoid RotationMatrixTranspose(const T R[9], T inv_R[9])\n{\n\tinv_R[0] = R[0];\n\tinv_R[1] = R[3];\n\tinv_R[2] = R[6];\n\tinv_R[3] = R[1];\n\tinv_R[4] = R[4];\n\tinv_R[5] = R[7];\n\tinv_R[6] = R[2];\n\tinv_R[7] = R[5];\n\tinv_R[8] = R[8];\n};\n\ntemplate <typename T>\nvoid RotationMatrixRotatePoint(const T R[9], const T t[3], T r_t[3])\n{\n\tr_t[0] = R[0] * t[0] + R[1] * t[1] + R[2] * t[2];\n\tr_t[1] = R[3] * t[0] + R[4] * t[1] + R[5] * t[2];\n\tr_t[2] = R[6] * t[0] + R[7] * t[1] + R[8] * t[2];\n};\n\nclass PNPEulerAngleError\n{\npublic:\n    PNPEulerAngleError( Vector3d _w_X, Vector2d _uv_normed_cords ): w_X(_w_X), uv_normed_cords(_uv_normed_cords)\n    {}\n\n        //    //      minimize_{R,t} \\sum_i (  PI( c_(R|t)_w * w_X[i] ) - u[i] )\n\n    template <typename T>\n    bool operator()( const T* yaw, const T* pitch, const T* roll, const T* tx, const T* ty , const T* tz, T * residue ) const\n    {\n        T R[9]; // c_T_a.\n        YawPitchRollToRotationMatrix( yaw[0], pitch[0], roll[0], R );\n\n        T out[3];\n        out[0] = R[0] * T(w_X(0)) + R[1] * T(w_X(1)) + R[2] * T(w_X(2)) + tx[0];\n        out[1] = R[3] * T(w_X(0)) + R[4] * T(w_X(1)) + R[5] * T(w_X(2)) + ty[0];\n        out[2] = R[6] * T(w_X(0)) + R[7] * T(w_X(1)) + R[8] * T(w_X(2)) + tz[0];\n\n\n        // Simple\n        residue[0] = out[0] / out[2] - T(uv_normed_cords(0));\n        residue[1] = out[1] / out[2] - T(uv_normed_cords(1));\n\n        // Weight by Z. A point that is far away need to be down weighted\n        // just search plot 1/ (1 + exp( x-10) )  on google. also called sigmoid function\n        T wt =  T( 1.0 / ( 1.0 + exp(w_X(2) - 10.) ) );\n        residue[0] *= wt;\n        residue[1] *= wt;\n\n        return true;\n    }\n\n    static ceres::CostFunction* Create( Vector3d _w_X, Vector2d _uv_normed_cords )\n\t{\n\t  return (new ceres::AutoDiffCostFunction<\n\t          PNPEulerAngleError, 2, 1,1,1,  1,1,1 >(\n\t          \tnew PNPEulerAngleError( _w_X, _uv_normed_cords ) ) );\n\t}\n\n\nprivate:\n    const Vector3d w_X;\n    const Vector2d uv_normed_cords;\n};\n\n\n\nclass P3PEulerAngleError\n{\npublic:\n    // [Input]:\n    //      _a_X: 3dpts expressed in co-ordinate frame of a\n    //      _b_X: 3dpts expressed in co-ordinate frame of b\n    P3PEulerAngleError( Vector3d _a_X, Vector3d _b_X ): a_X(_a_X), b_X(_b_X)\n    {}\n\n        //    //      minimize_{R,t} \\sum_i   b_(R|t)_a * a_X[i]  - b_X[i]\n\n    template <typename T>\n    bool operator()( const T* yaw, const T* pitch, const T* roll, const T* tx, const T* ty , const T* tz, T * residue ) const\n    {\n        T R[9]; // b_T_a.\n        YawPitchRollToRotationMatrix( yaw[0], pitch[0], roll[0], R );\n\n        T out[3];\n        out[0] = R[0] * T(a_X(0)) + R[1] * T(a_X(1)) + R[2] * T(a_X(2)) + tx[0];\n        out[1] = R[3] * T(a_X(0)) + R[4] * T(a_X(1)) + R[5] * T(a_X(2)) + ty[0];\n        out[2] = R[6] * T(a_X(0)) + R[7] * T(a_X(1)) + R[8] * T(a_X(2)) + tz[0];\n\n\n        // Simple\n        residue[0] = out[0] - T(b_X(0));\n        residue[1] = out[1] - T(b_X(1));\n        residue[2] = out[2] - T(b_X(2));\n\n\n        // Weight by Z. A point that is far away need to be down weighted\n        // just search plot 1/ (1 + exp( x-10) )  on google. also called sigmoid function\n        T wt =  T( 1.0 / ( 1.0 + exp(a_X(2) - 10.) ) );\n        residue[0] *= wt;\n        residue[1] *= wt;\n        residue[2] *= wt;\n\n        return true;\n    }\n\n    static ceres::CostFunction* Create( Vector3d _a_X, Vector3d _b_X )\n\t{\n\t  return (new ceres::AutoDiffCostFunction<\n\t          P3PEulerAngleError, 3, 1,1,1,  1,1,1 >(\n\t          \tnew P3PEulerAngleError( _a_X, _b_X ) ) );\n\t}\n\n\nprivate:\n    const Vector3d a_X;\n    const Vector3d b_X;\n};\n\n\n/*\nstruct PNPFourDOFError\n{\n\tPNPFourDOFError(double t_x, double t_y, double t_z, double relative_yaw, double pitch_i, double roll_i)\n\t\t\t\t  :t_x(t_x), t_y(t_y), t_z(t_z), relative_yaw(relative_yaw), pitch_i(pitch_i), roll_i(roll_i){}\n\n\ttemplate <typename T>\n\tbool operator()(const T* const yaw_i, const T* ti, const T* yaw_j, const T* tj, T* residuals) const\n\t{\n\t\tT t_w_ij[3];\n\t\tt_w_ij[0] = tj[0] - ti[0];\n\t\tt_w_ij[1] = tj[1] - ti[1];\n\t\tt_w_ij[2] = tj[2] - ti[2];\n\n\t\t// euler to rotation\n\t\tT w_R_i[9];\n\t\tYawPitchRollToRotationMatrix(yaw_i[0], T(pitch_i), T(roll_i), w_R_i);\n\t\t// rotation transpose\n\t\tT i_R_w[9];\n\t\tRotationMatrixTranspose(w_R_i, i_R_w);\n\t\t// rotation matrix rotate point\n\t\tT t_i_ij[3];\n\t\tRotationMatrixRotatePoint(i_R_w, t_w_ij, t_i_ij);\n\n\t\tresiduals[0] = (t_i_ij[0] - T(t_x));\n\t\tresiduals[1] = (t_i_ij[1] - T(t_y));\n\t\tresiduals[2] = (t_i_ij[2] - T(t_z));\n\t\tresiduals[3] = NormalizeAngle(yaw_j[0] - yaw_i[0] - T(relative_yaw));\n\n\t\treturn true;\n\t}\n\n\tstatic ceres::CostFunction* Create(const double t_x, const double t_y, const double t_z,\n\t\t\t\t\t\t\t\t\t   const double relative_yaw, const double pitch_i, const double roll_i)\n\t{\n\t  return (new ceres::AutoDiffCostFunction<\n\t          PNPFourDOFError, 4, 1, 3, 1, 3>(\n\t          \tnew PNPFourDOFError(t_x, t_y, t_z, relative_yaw, pitch_i, roll_i)));\n\t}\n\n\tdouble t_x, t_y, t_z;\n\tdouble relative_yaw, pitch_i, roll_i;\n\n};\n*/\n"
  },
  {
    "path": "src/PinholeCamera.cpp",
    "content": "#include \"PinholeCamera.h\"\n\n\nPinholeCamera::PinholeCamera( string config_file )\n{\n\n  cv::FileStorage fs( config_file, cv::FileStorage::READ );\n  if( !fs.isOpened() )\n  {\n    ROS_ERROR( \"Cannot open config file : %s\", config_file.c_str() );\n    ROS_ERROR( \"Quit\");\n    mValid = false;\n    exit(1);\n  }\n  this->config_file_name = string(config_file);\n\n  cout << \"---Camera Config---\\n\";\n  fs[\"model_type\"] >> config_model_type;     cout << \"config_model_type:\"<< config_model_type << endl;\n  fs[\"camera_name\"] >> config_camera_name;   cout << \"config_camera_name:\" << config_camera_name << endl;\n  fs[\"image_width\"] >> config_image_width;   cout << \"config_image_width:\" << config_image_width << endl;\n  fs[\"image_height\"] >> config_image_height; cout << \"config_image_height:\" << config_image_height << endl;\n  // assert( config_model_type == \"PINHOLE\" && (bool)\"The class PinholeCamera is designed only for PINHOLE camera model. In this config_file: \"+config_file + \" the camera model was \"+config_model_type+\" this was not expected\\n\" );\n\n  fs[\"projection_parameters\"][\"fx\"] >> _fx;\n  fs[\"projection_parameters\"][\"fy\"] >> _fy;\n  fs[\"projection_parameters\"][\"cx\"] >> _cx;\n  fs[\"projection_parameters\"][\"cy\"] >> _cy;\n\n  cout << \"projection_parameters :: \" << _fx << \" \" << _fy << \" \" << _cx << \" \" << _cy << \" \" << endl;\n\n  fs[\"distortion_parameters\"][\"k1\"] >> _k1;\n  fs[\"distortion_parameters\"][\"k2\"] >> _k2;\n  fs[\"distortion_parameters\"][\"p1\"] >> _p1;\n  fs[\"distortion_parameters\"][\"p2\"] >> _p2;\n  cout << \"distortion_parameters :: \" << _k1 << \" \" << _k2 << \" \" << _p1 << \" \" << _p2 << \" \" << endl;\n  cout << \"---    ---\\n\";\n\n  // Define the 3x3 Projection matrix eigen and/or cv::Mat.\n  m_K = cv::Mat::zeros( 3,3,CV_32F );\n  m_K.at<float>(0,0) = _fx;\n  m_K.at<float>(1,1) = _fy;\n  m_K.at<float>(0,2) = _cx;\n  m_K.at<float>(1,2) = _cy;\n  m_K.at<float>(2,2) = 1.0;\n\n  m_D = cv::Mat::zeros( 4, 1, CV_32F );\n  m_D.at<float>(0,0) = _k1;\n  m_D.at<float>(1,0) = _k2;\n  m_D.at<float>(2,0) = _p1;\n  m_D.at<float>(3,0) = _p2;\n  cout << \"m_K\" << m_K << endl;\n  cout << \"m_D\" << m_D << endl;\n\n  // Define 4x1 vector of distortion params eigen and/or cv::Mat.\n  e_K << _fx, 0.0, _cx,\n        0.0,  _fy, _cy,\n        0.0, 0.0, 1.0;\n\n  e_D << _k1 , _k2, _p1 , _p2;\n  cout << \"e_K:\\n\" << e_K << endl;\n  cout << \"e_D:\\n\" << e_D << endl;\n  mValid = true;\n\n}\n\n\nPinholeCamera::PinholeCamera( double __fx, double __fy, double __cx, double __cy,\n                  int __im_rows, int __im_cols,\n                  double __k1, double __k2, double __p1, double __p2\n              ): _fx( __fx ), _fy( __fy ), _cx( __cx ), _cy( __cy ),\n                 config_image_width( __im_cols ), config_image_height( __im_rows ),\n                 _k1( __k1 ), _k2( __k2 ), _p1( __p1 ), _p2( __p2 )\n{\n    ROS_WARN( \"[PinholeCamera::PinholeCamera] Setting camera intrinsics explicitly.....\\n\");\n\n    // Define the 3x3 Projection matrix eigen and/or cv::Mat.\n    m_K = cv::Mat::zeros( 3,3,CV_32F );\n    m_K.at<float>(0,0) = _fx;\n    m_K.at<float>(1,1) = _fy;\n    m_K.at<float>(0,2) = _cx;\n    m_K.at<float>(1,2) = _cy;\n    m_K.at<float>(2,2) = 1.0;\n\n    m_D = cv::Mat::zeros( 4, 1, CV_32F );\n    m_D.at<float>(0,0) = _k1;\n    m_D.at<float>(1,0) = _k2;\n    m_D.at<float>(2,0) = _p1;\n    m_D.at<float>(3,0) = _p2;\n    cout << \"m_K\" << m_K << endl;\n    cout << \"m_D\" << m_D << endl;\n\n    // Define 4x1 vector of distortion params eigen and/or cv::Mat.\n    e_K << _fx, 0.0, _cx,\n          0.0,  _fy, _cy,\n          0.0, 0.0, 1.0;\n\n    e_D << _k1 , _k2, _p1 , _p2;\n    cout << \"e_K:\\n\" << e_K << endl;\n    cout << \"e_D:\\n\" << e_D << endl;\n    mValid = true;\n\n\n}\n\n\nvoid PinholeCamera::printCameraInfo( int verbosity ) const\n{\n  cout << \"====== PinholeCamera::printCameraInfo ======\\n\";\n  if( !isValid() )\n    cout << \"camera not set\\n\";\n\n  cout << \"config_file: \" << this->config_file_name << endl;\n  cout << \"Image Rows: \" << getImageRows() << endl;\n  cout << \"Image Cols: \" << getImageCols() << endl;\n  cout << \"fx: \" << fx() << \" ;  fy: \" << fy() << endl;\n  cout << \"cx: \" << cx() << \" ;  cy: \" << cy() << endl;\n  cout << \"(distortion params)k1,k2,p1,p2\" << \" \" <<  k1() << \" \" <<  k2() << \" \" <<  p1() << \" \" <<  p2() << endl;\n\n  if( verbosity >= 1 ) {\n    cout << \"--cv::Mat \\n\";\n    cout << \"m_K\\n\" << m_K << endl;\n    cout << \"m_D\\n\" << m_D << endl;\n\n    cout << \"--Eigen::Matrix \\n\";\n    cout << \"e_K\\n\" << e_K << endl;\n    cout << \"e_K\\n\" << e_D << endl;\n  }\n\n  cout << \"================================================\\n\";\n\n}\n\nstring PinholeCamera::getCameraInfoAsJson() const\n{\n    if( !isValid() )\n        return string(\"{\\\"isValid\\\": 1}\\n\");\n    std::stringstream buffer;\n    buffer << \"{\\n\";\n    buffer << \"\\\"config_file\\\": \\\"\" << this->config_file_name << \"\\\"\"<< endl;\n    buffer << \",\\\"image_rows\\\": \" << getImageRows() << endl;\n    buffer << \",\\\"image_cols\\\": \" << getImageCols() << endl;\n    buffer << \",\\\"fx\\\": \" << fx() << \" \\n,fy: \" << fy() << endl;\n    buffer << \",\\\"cx\\\": \" << cx() << \" \\n,cy: \" << cy() << endl;\n    buffer << \",\\\"k1\\\": \" << k1() << endl;\n    buffer << \",\\\"k2\\\": \" << k2() << endl;\n    buffer << \",\\\"p1\\\": \" << p1() << endl;\n    buffer << \",\\\"p2\\\": \" << p2() << endl;\n    buffer << \"}\\n\" ;\n    return buffer.str();\n}\n\n\nvoid PinholeCamera::print_cvmat_info( string msg, const cv::Mat& A )\n{\n  cout << msg << \":\" << \"rows=\" << A.rows << \", cols=\" << A.cols << \", ch=\" << A.channels() << \", type=\" << type2str( A.type() ) << endl;\n}\n\nstring PinholeCamera::type2str(int type) {\n  string r;\n\n  uchar depth = type & CV_MAT_DEPTH_MASK;\n  uchar chans = 1 + (type >> CV_CN_SHIFT);\n\n  switch ( depth ) {\n    case CV_8U:  r = \"8U\"; break;\n    case CV_8S:  r = \"8S\"; break;\n    case CV_16U: r = \"16U\"; break;\n    case CV_16S: r = \"16S\"; break;\n    case CV_32S: r = \"32S\"; break;\n    case CV_32F: r = \"32F\"; break;\n    case CV_64F: r = \"64F\"; break;\n    default:     r = \"User\"; break;\n  }\n\n  r += \"C\";\n  r += (chans+'0');\n\n  return r;\n}\n\n\n// Input 3d points in homogeneous co-ordinates 4xN matrix. Eigen I/O\nvoid PinholeCamera::perspectiveProject3DPoints( const MatrixXd& c_X,\n                              MatrixXd& out_pts )\n{\n\n    // DIY - Do It Yourself Projection\n    // c_X.row(0).array() /= c_X.row(3).array();\n    // c_X.row(1).array() /= c_X.row(3).array();\n    // c_X.row(2).array() /= c_X.row(3).array();\n    // c_X.row(3).array() /= c_X.row(3).array();\n\n\n\n    // K [ I | 0 ]\n    MatrixXd I_0;\n    I_0 = Matrix4d::Identity().topLeftCorner<3,4>();\n    // MatrixXf P1;\n    // P1 = cam_intrin * I_0; //3x4\n\n    // Project and Perspective Divide\n    MatrixXd im_pts;\n    im_pts = I_0 * c_X; //in normalized image co-ordinate. Beware that distortion need to be applied in normalized co-ordinates\n    im_pts.row(0).array() /= im_pts.row(2).array();\n    im_pts.row(1).array() /= im_pts.row(2).array();\n    im_pts.row(2).array() /= im_pts.row(2).array();\n\n    // Apply Distortion\n    MatrixXd Xdd = MatrixXd( im_pts.rows(), im_pts.cols() );\n    for( int i=0 ; i<im_pts.cols() ; i++)\n    {\n      double r2 = im_pts(0,i)*im_pts(0,i) + im_pts(1,i)*im_pts(1,i);\n      double c = 1.0f + (double)k1()*r2 + (double)k2()*r2*r2;\n      Xdd(0,i) = im_pts(0,i) * c + 2.0f*(double)p1()*im_pts(0,i)*im_pts(1,i) + (double)p2()*(r2 + 2.0*im_pts(0,i)*im_pts(0,i));\n      Xdd(1,i) = im_pts(1,i) * c + 2.0f*(double)p2()*im_pts(0,i)*im_pts(1,i) + (double)p1()*(r2 + 2.0*im_pts(1,i)*im_pts(1,i));\n      Xdd(2,i) = 1.0f;\n    }\n\n    out_pts = e_K * Xdd;\n\n\n}\n"
  },
  {
    "path": "src/PinholeCamera.h",
    "content": "#pragma once\n/** Class to handle camera intrinsics\n\n      Author  : Manohar Kuse <mpkuse@connect.ust.hk>\n      Created : 3rd Oct, 2017\n      Modified: 26th Oct, 2018\n*/\n\n\n#include <iostream>\n#include <string>\n#include <fstream>\n\n\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n//ros\n#include <ros/ros.h>\n#include <ros/package.h>\n\n\n// Eigen3\n#include <Eigen/Dense>\n#include <Eigen/Geometry>\nusing namespace Eigen;\n#include <opencv2/core/eigen.hpp>\n\nusing namespace std;\n\n\n\nclass PinholeCamera {\n\npublic:\n  PinholeCamera() { mValid = false; }\n  PinholeCamera( string config_file );\n  PinholeCamera( double __fx, double __fy, double __cx, double __cy,\n                    int __im_rows, int __im_cols,\n                    double __k1, double __k2, double __p1, double __p2\n                );\n\n  void printCameraInfo( int verbosity=2 ) const;\n  string getCameraInfoAsJson() const;\n\n\n  const cv::Mat get_mK() const {return m_K; }\n  const cv::Mat get_mD() const {return m_D; }\n\n  const Matrix3d get_eK() const {return e_K; }\n  const Vector4d get_eD() const {return e_D; }\n\n  // have a const after function, signals the compiler that these functions will not attempt to modify the variables inside\n  bool isValid() const  { return mValid; }\n  double fx() const  { return _fx; }\n  double fy() const  { return _fy; }\n  double cx() const  { return _cx; }\n  double cy() const  { return _cy; }\n  double k1() const  { return _k1; }\n  double k2() const  { return _k2; }\n  double p1() const  { return _p1; }\n  double p2() const  { return _p2; }\n\n  string getModelType() const  { return config_model_type; }\n  string getCameraName() const  { return config_camera_name; }\n  string getConfigFileName() const  { return config_file_name; }\n  int getImageWidth() const  { return config_image_width; }\n  int getImageHeight() const  { return config_image_height; }\n\n  int getImageRows() const  { return this->getImageHeight(); }\n  int getImageCols() const  { return this->getImageWidth(); }\n\n\n  // Projection\n  // Input 3d points in homogeneous co-ordinates 4xN matrix. Eigen I/O.\n  // uses the camera matrix and D from this class\n  void perspectiveProject3DPoints( const MatrixXd& c_X, MatrixXd& out_pts );\n\nprivate:\n  string config_model_type, config_camera_name;\n  string config_file_name;\n  int config_image_width, config_image_height;\n\n  double _fx, _fy, _cx, _cy;\n  double _k1, _k2, _p1, _p2;\n\n  bool mValid;\n\n  void print_cvmat_info( string msg, const cv::Mat& A );\n  string type2str(int type);\n\n  cv::Mat m_K; //3x3\n  cv::Mat m_D; //4x1\n\n  Matrix3d e_K;\n  Vector4d e_D;\n\n\n};\n"
  },
  {
    "path": "src/ProcessedLoopCandidate.cpp",
    "content": "#include \"ProcessedLoopCandidate.h\"\n\nProcessedLoopCandidate::ProcessedLoopCandidate( int idx_from_raw_candidates_list,\n        DataNode * node_1, DataNode * node_2 )\n{\n    // cout << \"ProcessedLoopCandidate::ProcessedLoopCandidate\\n\";\n    this->node_1 = node_1;\n    this->node_2 = node_2;\n    this->idx_from_raw_candidates_list = idx_from_raw_candidates_list;\n\n    opX_b_T_a.clear();\n    debug_images.clear();\n}\n\n\nbool ProcessedLoopCandidate::makeLoopEdgeMsg(  cerebro::LoopEdge& msg )\n{\n    if( isSet_3d2d__2T1 == false ) {\n        cout << TermColor::RED() << \"[ProcessedLoopCandidate::makeLoopEdgeMsg]\\\n        You asked me to make cerebro::LoopEdge of ProcessedLoopCandidate in\\\n        which there is no valid pose data\" << TermColor::RESET() << endl;\n        return false;\n    }\n    msg.timestamp0 = node_1->getT();\n    msg.timestamp1 = node_2->getT();\n\n    geometry_msgs::Pose pose;\n    PoseManipUtils::eigenmat_to_geometry_msgs_Pose( _3d2d__2T1, pose );\n    msg.pose_1T0 = pose;\n\n    msg.weight = _3d2d__2T1__ransac_confidence; //1.0;\n    msg.description = to_string(idx_from_datamanager_1)+\"<=>\"+to_string(idx_from_datamanager_2);\n    msg.description += \"    this pose is: \"+to_string(idx_from_datamanager_2)+\"_T_\"+to_string(idx_from_datamanager_1);\n\n    return true;\n}\n\n// #define __ProcessedLoopCandidate_makeLoopEdgeMsgWithConsistencyCheck( msg )  msg;\n#define __ProcessedLoopCandidate_makeLoopEdgeMsgWithConsistencyCheck( msg ) ;\nbool ProcessedLoopCandidate::makeLoopEdgeMsgWithConsistencyCheck( cerebro::LoopEdge& msg )\n{\n    if( opX_b_T_a.size() != 3 ) {\n        cout << TermColor::RED() << \"[ProcessedLoopCandidate::makeLoopEdgeMsgWithConsistencyCheck] \\\n        I was expecting only 3 candidates\" << TermColor::RED() << endl;\n        return false;\n    }\n\n\n    ros::Duration diff = node_1->getT() - node_2->getT();\n    if(  abs(diff.sec) < 10 ) // the candidates are too near in time, so ignore them\n    {\n        __ProcessedLoopCandidate_makeLoopEdgeMsgWithConsistencyCheck(\n        cout << \"[ ProcessedLoopCandidate::makeLoopEdgeMsgWithConsistencyCheck] abs(diff.sec) < 10. diff= \"<< diff << endl;\n        )\n        return false;\n    }\n\n    ///////////////////////////////////////////////////////////\n    // Look at all the options and determine if they are consistent.\n\n\n    // if |del_angle| < 2.0 degrees and |del_translation| < 0.1m then not consistent\n    Matrix4d op1__b_T_a = opX_b_T_a[0];\n    Matrix4d op2__b_T_a = opX_b_T_a[1];\n    Matrix4d icp_b_T_a = opX_b_T_a[2];\n    Matrix4d op1_m_op2 = op1__b_T_a.inverse() * op2__b_T_a;\n    Matrix4d op1_m_icp = op1__b_T_a.inverse() * icp_b_T_a;\n    Matrix4d op2_m_icp = op2__b_T_a.inverse() * icp_b_T_a;\n    Vector3d op1_m_op2_ypr, op1_m_op2_tr; // ypr and translation of delta pose\n    Vector3d op1_m_icp_ypr, op1_m_icp_tr;\n    Vector3d op2_m_icp_ypr,op2_m_icp_tr;\n    PoseManipUtils::eigenmat_to_rawyprt( op1_m_op2, op1_m_op2_ypr, op1_m_op2_tr);\n    PoseManipUtils::eigenmat_to_rawyprt( op1_m_icp, op1_m_icp_ypr, op1_m_icp_tr);\n    PoseManipUtils::eigenmat_to_rawyprt( op2_m_icp, op2_m_icp_ypr, op2_m_icp_tr);\n    bool is_consistent_ypr = false, is_consistent_tr=false;\n\n    if( op1_m_op2_ypr.lpNorm<Eigen::Infinity>() < 5.0  &&\n        op1_m_icp_ypr.lpNorm<Eigen::Infinity>() < 5.0  &&\n        op2_m_icp_ypr.lpNorm<Eigen::Infinity>() < 5.0\n    )\n    { is_consistent_ypr = true;}\n\n    if( op1_m_icp_tr.lpNorm<Eigen::Infinity>() < .2  &&\n        op1_m_icp_tr.lpNorm<Eigen::Infinity>() < .2  &&\n        op2_m_icp_tr.lpNorm<Eigen::Infinity>() < .2\n    )\n    { is_consistent_tr = true;}\n\n    #if 0 //just priniting\n    __ProcessedLoopCandidate_makeLoopEdgeMsgWithConsistencyCheck(\n    Matrix4d odom_b_T_a = node_2->getPose().inverse() * node_1->getPose();\n    cout << \"---\" << to_string(idx_from_datamanager_1)+\"<=>\"+to_string(idx_from_datamanager_2);\n    cout << \"[ProcessedLoopCandidate::makeLoopEdgeMsgWithConsistencyCheck]\\n\" << TermColor::YELLOW() << \"odom_b_T_a = \" << PoseManipUtils::prettyprintMatrix4d( odom_b_T_a ) << TermColor::RESET() << endl;\n\n    cout << \"op1\" << \"confidence=\" << opX_goodness[0]<< \" \" << PoseManipUtils::prettyprintMatrix4d( op1__b_T_a ) << endl;\n    cout << \"op2\" << \"confidence=\" << opX_goodness[1]<< \" \" << PoseManipUtils::prettyprintMatrix4d( op2__b_T_a ) << endl;\n    cout << \"icp\" << \"confidence=\" << opX_goodness[2]<< \" \" << PoseManipUtils::prettyprintMatrix4d( icp_b_T_a ) << endl;\n\n    cout << \"|op1 - op2|\" << PoseManipUtils::prettyprintMatrix4d( op1__b_T_a.inverse() * op2__b_T_a ) << endl;\n    cout << \"|op1 - icp|\" << PoseManipUtils::prettyprintMatrix4d( op1__b_T_a.inverse() * icp_b_T_a ) << endl;\n    cout << \"|op2 - icp|\" << PoseManipUtils::prettyprintMatrix4d( op2__b_T_a.inverse() * icp_b_T_a ) << endl;\n    cout << TermColor::YELLOW() ;\n    cout << \"is_consistent_ypr=\" << is_consistent_ypr << \"\\t\" ;\n    cout << \"is_consistent_tr=\" << is_consistent_tr << \"\\t\";\n    cout << TermColor::RESET() << endl;\n    )\n    #endif\n\n\n    ///////////////////////////////// Done with consistency check /////////////////////\n\n    if( pf_matches > 800 && (is_consistent_ypr && is_consistent_tr) ) {\n    // put the final pose into `3d2d__2T1`\n        _3d2d__2T1 = opX_b_T_a[0];\n        isSet_3d2d__2T1 = true;\n        _3d2d__2T1__ransac_confidence = max( max( opX_goodness[0], opX_goodness[1] ), opX_goodness[2] );\n\n        // call the above function,\n        return makeLoopEdgeMsg( msg );\n    }\n    else {\n        cout << \"[ ProcessedLoopCandidate::makeLoopEdgeMsgWithConsistencyCheck] Insonsistent poses from 3 methods, so don't publish this pose\" <<  endl;\n        return false;\n    }\n}\n\n\nbool ProcessedLoopCandidate::asJson( json& out_json )\n{\n    json x;\n    if( node_1 == NULL || node_2 == NULL )\n        return false;\n\n    x[\"timestamp_a\"] = node_1->getT().toSec();\n    x[\"timestamp_b\"] = node_2->getT().toSec();\n    x[\"idx_a\"] = idx_from_datamanager_1;\n    x[\"idx_b\"] = idx_from_datamanager_2;\n    x[\"pf_matches\"] = pf_matches;\n    x[\"final_pose_available\"] = isSet_3d2d__2T1;\n\n    if( isSet_3d2d__2T1 ) {\n        x[\"3d2d__2T1\"] = PoseManipUtils::prettyprintMatrix4d( _3d2d__2T1 );\n        x[\"_3d2d__2T1__ransac_confidence\"] = _3d2d__2T1__ransac_confidence;\n    }\n\n\n    // write the poses which were computed with various methods\n    for( int i=0 ; i<opX_b_T_a.size() ; i++ )\n    {\n        json u;\n        u[\"name\"] = opX_b_T_a_name[i];\n        u[\"goodness\"] = opX_goodness[i];\n        u[\"debugmsg\"] = opX_b_T_a_debugmsg[i];\n        u[\"b_T_a\"] = PoseManipUtils::prettyprintMatrix4d( opX_b_T_a[i] );\n\n        #if 1\n        Vector3d _ypr, _t;\n        PoseManipUtils::eigenmat_to_rawyprt( opX_b_T_a[i], _ypr, _t );\n        u[\"b_T_a_yaw\"] =   _ypr(0);\n        u[\"b_T_a_pitch\"] = _ypr(1);\n        u[\"b_T_a_roll\"] =  _ypr(2);\n        u[\"b_T_a_tx\"] =      _t(0);\n        u[\"b_T_a_ty\"] =      _t(1);\n        u[\"b_T_a_tz\"] =      _t(2);\n        #endif\n\n        x[\"opX\"].push_back( u );\n    }\n\n    out_json = x;\n    return true;\n}\n"
  },
  {
    "path": "src/ProcessedLoopCandidate.h",
    "content": "#pragma once\n\n/** Stores info on the loop candidates. Also contains geometric info like\n    number of pf-matches, how it was calculated, and the relative pose\n**/\n\n\n#include <iostream>\n#include <thread>\n#include <mutex>\n#include <atomic>\n\n\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <cv_bridge/cv_bridge.h>\n\n// ros\n// #include <ros/ros.h>\n// #include <ros/package.h>\n\n// ros msg\n// #include <nav_msgs/Odometry.h>\n// #include <geometry_msgs/Pose.h>\n// #include <geometry_msgs/PoseWithCovariance.h>\n// #include <geometry_msgs/PoseStamped.h>\n// #include <sensor_msgs/PointCloud.h>\n// #include <geometry_msgs/Point32.h>\n// #include <sensor_msgs/Image.h>\n// // #include <nav_msgs/Path.h>\n// #include <geometry_msgs/Point.h>\n// #include <visualization_msgs/Marker.h>\n// #include <visualization_msgs/MarkerArray.h>\n\n// Eigen\n#include <Eigen/Core>\n#include <Eigen/Dense>\n#include <Eigen/Geometry>\nusing namespace Eigen;\n#include <opencv2/core/eigen.hpp>\n\n#include \"DataNode.h\"\n#include \"utils/PoseManipUtils.h\"\n// #include \"utils/MiscUtils.h\"\n#include \"utils/TermColor.h\"\n\n#include \"../utils/nlohmann/json.hpp\"\nusing json = nlohmann::json;\n\n// Publishing message\n#include <cerebro/LoopEdge.h>\n\nclass ProcessedLoopCandidate\n{\npublic:\n    ProcessedLoopCandidate( int idx_from_raw_candidates_list, DataNode * node_1, DataNode * node_2 );\n    ProcessedLoopCandidate() { idx_from_raw_candidates_list=-1; opX_b_T_a.clear(); debug_images.clear(); }\n\n    bool makeLoopEdgeMsg(  cerebro::LoopEdge& msg );\n\n    // Looks at all the candidate relative poses and decides if they are all consistent.\n    // Returns true if consistent, returns false if not consistent\n    bool makeLoopEdgeMsgWithConsistencyCheck( cerebro::LoopEdge& msg );\n\n    bool asJson( json& out_json );\n\n// private:\n    DataNode * node_1;\n    DataNode * node_2;\n\n    int idx_from_datamanager_1;\n    int idx_from_datamanager_2;\n\n    int idx_from_raw_candidates_list;\n\n    int pf_matches; //pf==>point-features\n\n\n    int _3d2d_n_pfvalid_depth;\n    Matrix4d _3d2d__2T1; //used 3d points from 1st view, 2d points from 2nd view.\n    bool isSet_3d2d__2T1=false; //< final_pose_available\n    float _3d2d__2T1__ransac_confidence;\n\n\n    // std::mutex _mutex;\n\n    // All computed poses\n    vector<Matrix4d> opX_b_T_a; // b_T_a is same as 2T1\n    vector<float> opX_goodness;\n    vector<string> opX_b_T_a_name;\n    vector<string> opX_b_T_a_debugmsg;\n\n    // debug images\n    vector<cv::Mat> debug_images;\n    vector<string> debug_images_titles;\n\n\n\n};\n"
  },
  {
    "path": "src/Visualization.cpp",
    "content": "#include \"Visualization.h\"\n\nVisualization::Visualization( ros::NodeHandle &nh )\n{\n    b_run_thread = false;\n    this->nh = nh;\n\n}\n\nvoid Visualization::setDataManager( DataManager* dataManager )\n{\n    this->dataManager = dataManager;\n    m_dataManager_available = true;\n}\n\nvoid Visualization::setCerebro( Cerebro* cerebro )\n{\n    this->cerebro = cerebro;\n    m_cerebro_available = true;\n}\n\n\nvoid Visualization::setVizPublishers( const string base_topic_name )\n{\n    //\n    // Test Publisher\n    string pub_topic_test = base_topic_name+\"/chatter\";\n    ROS_INFO( \"Visualization Publisher pub_topic_test: %s\", pub_topic_test.c_str() );\n    chatter_pub = nh.advertise<std_msgs::String>(pub_topic_test, 1000);\n\n    //\n    // Publish Markers of frame data\n    string framedata_pub_topic = base_topic_name+\"/framedata\";\n    ROS_INFO( \"Visualization Publisher framedata_pub_topic: %s\", framedata_pub_topic.c_str() );\n    framedata_pub = nh.advertise<visualization_msgs::Marker>(framedata_pub_topic, 1000);\n\n    // can add image publish if need be here.\n    string imagepaire_pub_topic = base_topic_name+\"/imagepaire\";\n    ROS_INFO( \"Visualization Publisher imagepair_pub_topic: %s\", imagepaire_pub_topic.c_str() );\n    imagepaire_pub = nh.advertise<sensor_msgs::Image>(imagepaire_pub_topic, 1000);\n}\n\nvoid Visualization::run( const int looprate    )\n{\n    assert( m_dataManager_available && \"You need to set the DataManager in class Visualization before execution of the run() thread can begin. You can set the dataManager by call to Visualization::setDataManager()\\n\");\n    assert( b_run_thread && \"you need to call run_thread_enable() before run() can start executing\\n\" );\n    assert( looprate > 0  && \"[ Visualization::run] looprate need to be postive\\n\");\n\n    // Adjust these for debugging.\n    bool bpub_framepositions = false;          //< This publishes text-marker with node id\n    bool bpub_loopcandidates = true;           //< this publishes line marker\n    bool bpub_processed_loopcandidates = true; //this publoshes the image-pair as image.\n\n    ros::Rate rate( looprate );\n    while( b_run_thread )\n    {\n        // cout << \"Visualization::run() \" << dataManager->getDataMapRef().size() <<endl;\n        if( bpub_framepositions )\n            this->publish_frames();\n\n        if( bpub_loopcandidates )\n            this->publish_loopcandidates(); //< this publishes marker\n        // this->publish_test_string();\n\n        if( bpub_processed_loopcandidates )\n            this->publish_processed_loopcandidates(); //< this publoshes the image-pair as image.\n\n        rate.sleep();\n    }\n}\n\n\n// #define __Visualization___publish_processed_loopcandidates(msg) msg;\n#define __Visualization___publish_processed_loopcandidates(msg) ;\nvoid Visualization::publish_processed_loopcandidates()\n{\n    assert( m_dataManager_available && m_cerebro_available && \"You need to set cerebro and DataManager in class Visualization  before execution of the run() thread can begin. You can set the cerebro by call to Visualization::setCerebro() and dataManager as setDataManager.\\n\");\n\n    static int prev_count = -1;\n\n    if( prev_count == cerebro->processedLoops_count() || prev_count<0 ) {\n        prev_count = cerebro->processedLoops_count();\n        // nothing new\n        return;\n    }\n\n    int new_count = cerebro->processedLoops_count();\n    // [prev_count , new_count ] are new\n\n    // cout << \"[Visualization::publish_processed_loopcandidates]\" << new_count << endl;\n    __Visualization___publish_processed_loopcandidates(\n    cout << \"#new procs_loops=\" << new_count - prev_count << \"  from [\" << prev_count << \",\" << new_count-1 << \"]\\n\";\n    )\n\n    visualization_msgs::Marker marker;\n    RosMarkerUtils::init_line_marker( marker );\n    marker.ns = \"processed_loopcandidates_line\";\n\n    // loop over all the new\n    int loop_start = prev_count;\n    int loop_end = new_count;\n    bool publish_image = true;\n    bool publish_loop_line_marker = false;\n    // if( rand()%100 < 2 ) {\n        // loop_start=0;\n        // publish_image=false;\n    // }\n\n    for( int i=loop_start ; i< loop_end; i++ )\n    {\n        // publish green colored line\n        ProcessedLoopCandidate candidate_i =  cerebro->processedLoops_i( i );\n        __Visualization___publish_processed_loopcandidates(\n        cout << \"---\\nUsing processedLoop[\"<<i<<\"]\"<<endl;\n        cout << candidate_i.node_1->getT() << \"<--->\" << candidate_i.node_2->getT() << endl;\n        cout << \"isPoseAvailable: \" << candidate_i.node_1->isPoseAvailable() << endl;\n        )\n\n\n        if( publish_loop_line_marker )\n        {\n            Vector4d w_t_1 = candidate_i.node_1->getPose().col(3);\n            Vector4d w_t_2 = candidate_i.node_2->getPose().col(3);\n            RosMarkerUtils::add_point_to_marker( w_t_1, marker, true );\n            RosMarkerUtils::add_point_to_marker( w_t_2, marker, false );\n            RosMarkerUtils::setcolor_to_marker( 0.0, 1.0, 0.0 , marker );\n            marker.ns = \"processed_loopcandidates_line\";\n            marker.id = i;\n            framedata_pub.publish( marker );\n\n\n            // Publish marker line\n            if( candidate_i.isSet_3d2d__2T1 == false )\n            {\n                cout << \"[Visualization::publish_processed_loopcandidates] _3d2d__2T1 is not set. This means the final pose is not set. This is because the candidate relative poses do not appear to be consistent with each other. Ignoring this ProcessedLoopCandidate\" << endl;;\n                continue;\n            }\n\n            Matrix4d w_T_2__new = candidate_i.node_1->getPose() * (candidate_i._3d2d__2T1).inverse();\n            Vector4d w_t_2__new = w_T_2__new.col(3);\n            RosMarkerUtils::add_point_to_marker( w_t_1, marker, true );\n            RosMarkerUtils::add_point_to_marker( w_t_2__new, marker, false );\n            RosMarkerUtils::setcolor_to_marker( 1.0, 1.0, 1.0 , marker );\n            marker.ns = \"processed_loopcandidates_new_position_of_2\";\n            marker.id = i;\n            framedata_pub.publish( marker );\n        }\n\n\n        // publish image\n        if( publish_image)\n        {\n            #if  0\n            if( candidate_i.node_1->isImageAvailable() && candidate_i.node_2->isImageAvailable() )\n            #else\n            auto img_data_mgr = dataManager->getImageManagerRef();\n            if(\n                img_data_mgr->isImageRetrivable(\"left_image\", candidate_i.node_1->getT()) &&\n                img_data_mgr->isImageRetrivable(\"left_image\", candidate_i.node_2->getT())\n              )\n            #endif\n            {\n\n                #if 0 // code where data_node had image data, mark this for removal\n                cv::Mat _node_1_im = candidate_i.node_1->getImage();\n                cv::Mat _node_2_im = candidate_i.node_2->getImage();\n                #else // image manager has the image data\n                cv::Mat _node_1_im, _node_2_im;\n                bool status1 = img_data_mgr->getImage( \"left_image\", candidate_i.node_1->getT(), _node_1_im );\n                bool status2 = img_data_mgr->getImage( \"left_image\", candidate_i.node_2->getT(), _node_2_im );\n                assert( status1 && status2 && \"[Visualization::publish_processed_loopcandidates]\\n\");\n                #endif\n\n                // use image from node\n                cv::Mat side_by_side_impair;\n                MiscUtils::side_by_side( _node_1_im, _node_2_im, side_by_side_impair );\n\n                string sgg = std::to_string( candidate_i.idx_from_datamanager_1  )\n                                + \"                    \"+\n                             std::to_string( candidate_i.idx_from_datamanager_2  );\n                 string sgg_time = std::to_string( candidate_i.node_1->getT().toSec()  )\n                                 + \"             \"+\n                              std::to_string( candidate_i.node_2->getT().toSec()  );\n\n\n                MiscUtils::append_status_image( side_by_side_impair, \";;\"+sgg,\n                        2.0, cv::Scalar(0,0,0), cv::Scalar(255,255,255), 3.0 );\n                MiscUtils::append_status_image( side_by_side_impair, \";\"+sgg_time+\";;processedLoop[\"+to_string(i)+\"]\",\n                        1.5, cv::Scalar(0,0,0), cv::Scalar(255,255,255), 3.0 );\n\n                if( candidate_i.isSet_3d2d__2T1 ) {\n                    MiscUtils::append_status_image( side_by_side_impair, \";Pose Estimation was consistent, so Accept\",\n                            1.5, cv::Scalar(0,0,0), cv::Scalar(0,255,0), 3.0 );\n                } else {\n                    MiscUtils::append_status_image( side_by_side_impair, \";Inconsistent Pose Estimation, so Reject\",\n                            1.5, cv::Scalar(0,0,0), cv::Scalar(0,0,255), 3.0 );\n                }\n\n                cv::Mat buffer;\n                cv::resize(side_by_side_impair, buffer, cv::Size(), 0.5, 0.5 );\n\n                cv_bridge::CvImage cv_image;\n                cv_image.image = buffer;\n                if( buffer.channels() == 1 )\n                    cv_image.encoding = \"mono8\";\n                else if( buffer.channels() == 3 )\n                    cv_image.encoding = \"bgr8\";\n                else {\n                    cout << TermColor::RED() << \"[Visualization::publish_processed_loopcandidates]invalid number of channels EXIT\\n\";\n                    exit(1);\n                }\n\n                sensor_msgs::Image ros_image_msg;\n                cv_image.toImageMsg(ros_image_msg);\n                __Visualization___publish_processed_loopcandidates(\n                    cout << MiscUtils::imgmsg_info( ros_image_msg ) << endl;\n                )\n                imagepaire_pub.publish( ros_image_msg );\n            }\n        }\n\n\n    }\n    prev_count = new_count;\n}\n\n\n// #define __Visualization__publish_loopcandidates(msg) msg\n#define __Visualization__publish_loopcandidates(msg) ;\nvoid Visualization::publish_loopcandidates()\n{\n    assert( m_dataManager_available && m_cerebro_available && \"You need to set cerebro and DataManager in class Visualization  before execution of the run() thread can begin. You can set the cerebro by call to Visualization::setCerebro() and dataManager as setDataManager.\\n\");\n\n    // last 10 publish\n    int n = cerebro->foundLoops_count();\n\n    int start = max( 0, n - 10 );\n    // 5% of the time start from 0.\n    if( rand() % 100 < 2 ) start = 0;\n    __Visualization__publish_loopcandidates(cout << \"[Visualization::publish_loopcandidates] start=\" << start << \" end=\" << n << endl;)\n\n    if( n <= 0 ) return;\n\n    auto data_map = dataManager->getDataMapRef();\n    visualization_msgs::Marker marker;\n    RosMarkerUtils::init_line_marker( marker );\n    marker.ns = \"loopcandidates_line\";\n    RosMarkerUtils::setcolor_to_marker( 1.0, 0.0, 0.0 , marker );\n    // TODO If need be can also publish text for each (which could be score of the edge)\n\n\n    // for( int i=0 ; i<n ; i++ )\n    for( int i=start ; i<n ; i++ )\n    {\n\n        marker.id = i;\n\n        auto u = cerebro->foundLoops_i( i );\n        ros::Time t_curr = std::get<0>(u);\n        ros::Time t_prev = std::get<1>(u);\n        double score = std::get<2>(u);\n\n        assert( data_map.count( t_curr ) > 0 && data_map.count( t_prev ) > 0  && \"One or both of the timestamps in foundloops where not in the data_map. This cannot be happening...fatal...\\n\" );\n        int idx_1 = std::distance( data_map->begin(), data_map->find( t_curr )  );\n        int idx_2 = std::distance( data_map->begin(), data_map->find( t_prev )  );\n\n        Vector4d w_t_curr = data_map->at(t_curr)->getPose().col(3);\n        Vector4d w_t_prev = data_map->at(t_prev)->getPose().col(3);\n\n        // TODO - need to test. looks like alright.\n        // add_point_to_marker with w_t_curr\n        // add_point_to_marker with w_t_prev\n        RosMarkerUtils::add_point_to_marker( w_t_curr, marker, true );\n        RosMarkerUtils::add_point_to_marker( w_t_prev, marker, false );\n\n\n\n        framedata_pub.publish( marker );\n    }\n\n}\n\n// #define __Visualization__publish_frames( cmd ) cmd\n#define __Visualization__publish_frames( cmd ) ;\nvoid Visualization::publish_frames()\n{\n    assert( m_dataManager_available && \"You need to set the DataManager in class Visualization before execution of the run() thread can begin. You can set the dataManager by call to Visualization::setDataManager()\\n\");\n\n    // Adjust these manually to change behaviour\n    bool publish_camera_visual = false;\n    bool publish_camera_as_point = true;\n    bool publish_txt = true;\n    bool publish_verbose_txt = false;\n\n\n    static std::map< ros::Time, int > XC;\n\n    __Visualization__publish_frames(cout << TermColor::RED() << \"---\" << TermColor::RESET() << endl;)\n    __Visualization__publish_frames(cout << \"start... sizeof(XC)=\" << XC.size() << endl;)\n    auto data_map = dataManager->getDataMapRef();\n    auto pose0 = dataManager->getPose0Stamp();\n\n\n    visualization_msgs::Marker cam_vis; //visualize every pose as a cam-visual\n    RosMarkerUtils::init_camera_marker( cam_vis , .5 );\n    cam_vis.ns = \"cam_pose_vis\";\n\n\n    visualization_msgs::Marker pt_vis; //visualize every pose as a point.\n    RosMarkerUtils::init_points_marker( pt_vis );\n    geometry_msgs::Point zer; zer.x =0.; zer.y=0.; zer.z = 0;\n    pt_vis.points.push_back( zer );\n    pt_vis.ns = \"cam_pose_pt\";\n    pt_vis.scale.x = 0.015;\n    pt_vis.scale.y = 0.015;\n\n\n    visualization_msgs::Marker txt_vis;\n    RosMarkerUtils::init_text_marker( txt_vis );\n    txt_vis.ns = \"cam_pose_txt\";\n    txt_vis.scale.z = 0.03;\n\n\n    for( auto it = data_map->begin() ; it != data_map->end() ; it++ )\n    {\n        if( XC[ it->first ] < 10 || rand() % 100 < 2 ) { // publish only if not already published 10 times\n            int seq_id = std::distance( data_map->begin() , it );\n            cam_vis.id = seq_id;\n            pt_vis.id = seq_id;\n            txt_vis.id = seq_id;\n\n            // Set Colors\n            if( it->second->isKeyFrame() ) {\n                RosMarkerUtils::setcolor_to_marker( 0., 1., 0. , cam_vis );\n                RosMarkerUtils::setcolor_to_marker( 0., 1., 0. , pt_vis );\n                RosMarkerUtils::setcolor_to_marker( 1., 1., 1. , txt_vis );\n\n                // if( it->second->isWholeImageDescriptorAvailable() )\n                    // RosMarkerUtils::setcolor_to_marker( 0., 0, 1. , cam_vis );\n            }\n            else {\n                RosMarkerUtils::setcolor_to_marker( 1., 0., 0. , cam_vis );\n                RosMarkerUtils::setcolor_to_marker( 1., 0., 0. , pt_vis );\n                RosMarkerUtils::setcolor_to_marker( 1., 1., 1. , txt_vis );\n            }\n\n\n            // Set Pose\n            if( it->second->isPoseAvailable() ) {\n                auto wTc = it->second->getPose();\n                RosMarkerUtils::setpose_to_marker( wTc , cam_vis );\n                RosMarkerUtils::setpose_to_marker( wTc , pt_vis );\n\n                RosMarkerUtils::setpose_to_marker( wTc , txt_vis );\n                txt_vis.text = \"\";\n                txt_vis.text += std::to_string(seq_id) + \";\";\n                if( publish_verbose_txt )  {\n                    txt_vis.text += std::to_string( (it->first - pose0).toSec() ) +\";\";\n                    txt_vis.text += std::to_string( (it->first).toSec() ) +\";\";\n                }\n\n                if( publish_camera_visual )\n                    framedata_pub.publish( cam_vis );\n                if( publish_camera_as_point )\n                    framedata_pub.publish( pt_vis );\n                if( publish_txt || publish_verbose_txt )\n                    framedata_pub.publish( txt_vis );\n            }\n\n\n            if( it->second->isKeyFrame() )\n                __Visualization__publish_frames(cout << TermColor::GREEN() );\n            __Visualization__publish_frames( cout << \"Publish seq_id=\" << seq_id << \"\\t xc=\" << XC[ it->first ] << \"\\t t=\"<< it->first - pose0 << \"\\t\" << it->first << TermColor::RESET() << endl; )\n\n            XC[ it->first ]++;\n        }\n    }\n    __Visualization__publish_frames( cout << \"Done... sizeof(XC)=\" << XC.size() << endl; )\n}\n/*\nvoid Visualization::publish_frames()\n{\n    assert( m_dataManager_available && \"You need to set the DataManager in class Visualization before execution of the run() thread can begin. You can set the dataManager by call to Visualization::setDataManager()\\n\");\n\n    static std::map<ros::Time, bool > XC;\n    static visualization_msgs::Marker linestrip_marker;\n    RosMarkerUtils::init_line_strip_marker( linestrip_marker );\n    linestrip_marker.ns = \"cam_line_strip\";\n    linestrip_marker.id = 0;\n\n\n    auto data_map = dataManager->getDataMapRef();\n    cout << \"---\\n\";\n    if( data_map.begin() == data_map.end() ) {\n        cout << \"nothing to vizualize\\n\";\n        return;\n    }\n\n    // cout << data_map.rbegin()->first << \"\\t\"<<  data_map.rbegin()->first - dataManager->getPose0Stamp() << endl;\n    ros::Time lb = data_map.rbegin()->first - ros::Duration(3);\n    for( auto it = data_map.lower_bound( lb ); it != data_map.end() ; it++ ) {\n        // cout << std::distance( it, data_map.begin() ) << \"] \" << it->first << \"\\t\" << it->first - dataManager->getPose0Stamp() << endl;\n\n        if( XC.count( it->first) == 0 ) {\n            cout << \"sizeof(XC)=\" << XC.size() << \"  \"<< it->first << \"\\t\" << it->first - dataManager->getPose0Stamp() << endl;\n\n            if( it->second->isPoseAvailable() ) {\n                auto w_T_c = it->second->getPose();\n                geometry_msgs::Point pt;\n                pt.x = w_T_c(0,3);\n                pt.y = w_T_c(1,3);\n                pt.z = w_T_c(2,3);\n                linestrip_marker.points.push_back( pt );\n                framedata_pub.publish( linestrip_marker );\n                XC[it->first] = true;\n            }\n        }\n    }\n    return ;\n\n}\n*/\n\nvoid Visualization::publish_test_string()\n{\n    std_msgs::String msg;\n    std::stringstream ss;\n    ss << \"hello world \" << ros::Time::now();\n    msg.data = ss.str();\n    chatter_pub.publish( msg );\n}\n"
  },
  {
    "path": "src/Visualization.h",
    "content": "#pragma once\n\n/**     Visualization class\n        This holds a reference to the DataManager. Will publish messages\n        to be seen with rviz.\n\n        Author  : Manohar Kuse<mpkuse@connect.ust.hk>\n        Created : 29th Oct, 2018\n\n        based on\n        https://github.com/mpkuse/nap/blob/master-desktop/src/DataManager_rviz_visualization.cpp\n\n*/\n\n\n\n#include <ros/ros.h>\n#include <ros/package.h>\n\n// ros msg\n#include <std_msgs/String.h>\n#include <visualization_msgs/Marker.h>\n\n#include \"utils/RosMarkerUtils.h\"\n#include \"utils/TermColor.h\"\n\n\n#include \"PinholeCamera.h\"\n#include \"DataManager.h\"\n#include \"Cerebro.h\"\n\n\nclass Visualization\n{\npublic:\n    Visualization( ros::NodeHandle &nh );\n\n    void setDataManager( DataManager* dataManager );\n    void setCerebro( Cerebro* cerebro );\n\n    void setVizPublishers( const string base_topic_name );\n\n    // This is supposed to be run in a separate thread.\n    void run_thread_enable() { b_run_thread = true; }\n    void run_thread_disable() { b_run_thread = false; }\n    void run( const int looprate  );\n\nprivate:\n    ros::NodeHandle nh;\n\n    bool m_dataManager_available=false;\n    DataManager * dataManager;\n\n    bool m_cerebro_available=false;\n    Cerebro * cerebro;\n\n    atomic<bool> b_run_thread;\n\n\nprivate:\n    void publish_frames(); //< publishes last 10 frames as markers. (occasionally all)\n    void publish_loopcandidates(); //< publishes last 10 loop candidates. cerebro->foundLoops_count().\n    void publish_processed_loopcandidates(); //< uses Cerebro::processedLoops_i() and publishes only the newly found loops. cerebro->processedLoops_i( i )\n    void publish_test_string();\n\n\n    //publishers\nprivate:\n    ros::Publisher chatter_pub;\n    ros::Publisher framedata_pub;\n    ros::Publisher imagepaire_pub;\n\n};\n"
  },
  {
    "path": "src/cerebro_node.cpp",
    "content": "/** Main for cerebro node.\n\n        This contains the main for the cerebro. It subscribes to various topics\n        from VINS-estimator and setups of threads a) vis-thread, b) redis-association-thread, c) etc\n\n        Author  : Manohar Kuse <mpkuse@connect.ust.hk>\n        Created : 26th Oct, 2018\n**/\n\n\n#include <ros/ros.h>\n#include <ros/package.h>\n\n\n#include \"PinholeCamera.h\"\n#include \"camodocal/camera_models/Camera.h\" // this is in include/camodocal. src files in src/utils/camodocal_src\n#include \"camodocal/camera_models/CameraFactory.h\"\n#include <boost/filesystem.hpp>\n\n\n#include \"DataManager.h\"\n#include \"Cerebro.h\"\n#include \"Visualization.h\"\n\n#include \"utils/nlohmann/json.hpp\"\nusing json = nlohmann::json;\n\n\nint main( int argc, char ** argv )\n{\n    //--- ROS INIT ---//\n    ros::init( argc, argv, \"cerebro_node\" );\n    ros::NodeHandle nh(\"~\");\n    // ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME, ros::console::levels::Debug);\n\n\n    //-- Set debug directory --//\n    // string debug_output_dir;\n    // nh.getParam( \"debug_output_dir\", debug_output_dir );\n    // ROS_WARN( \"debug_output_dir : %s\", debug_output_dir.c_str() );\n\n\n    //--- loadStateFromDisk, saveStateToDisk ---//\n    string loadStateFromDisk = \"\";\n    if( nh.getParam( \"loadStateFromDisk\", loadStateFromDisk ) == true )\n    {\n        if( loadStateFromDisk.compare(\"\") == 0 ) {\n            ROS_WARN( \"[cerebro_node] loadStateFromDisk cmdline parameter was found, but with a empty string, so I will not loadStateFromDisk()\");\n        } else {\n            // now make sure it is a directory\n            if( RawFileIO::is_path_a_directory(loadStateFromDisk) ) {\n                ROS_INFO( \"[cerebro_node] loadStateFromDisk=%s, Directory exists...OK!\", loadStateFromDisk.c_str() );\n                cout << TermColor::GREEN() <<  \"[cerebro_node] loadStateFromDisk=\" << loadStateFromDisk << \", Directory exists...OK!\" << TermColor::RESET() << endl;\n            }\n            else {\n                ROS_ERROR( \"[cerebro_node] You specified a directory for loadStateFromDisk=`%s`, This path need to exist\\n...EXIT\\n\", loadStateFromDisk.c_str());\n                exit(1);\n            }\n        }\n    } else {\n        ROS_WARN( \"[cerebro_node] loadStateFromDisk cmdline parameter was not found, so I will not loadStateFromDisk()\");\n    }\n\n    string saveStateToDisk = \"\";\n    if( nh.getParam( \"saveStateToDisk\", saveStateToDisk ) == true )\n    {\n        if( saveStateToDisk.compare(\"\") == 0 ) {\n            ROS_WARN( \"[cerebro_node] saveStateToDisk cmdline parameter was found, but with a empty string, so I will not saveStateToDisk()\");\n        } else {\n            // now make sure it is a directory\n            if( RawFileIO::is_path_a_directory(saveStateToDisk) ) {\n                ROS_INFO( \"[cerebro_node] saveStateToDisk=%s, Directory exists...OK!\", saveStateToDisk.c_str() );\n                cout << TermColor::GREEN() <<  \"[cerebro_node] saveStateToDisk=\" << saveStateToDisk << \", Directory exists...OK!\" << TermColor::RESET() << endl;\n            }\n            else {\n                ROS_ERROR( \"[cerebro_node] You specified a directory for saveStateToDisk=`%s`, This path need to exist and be writable\\n...EXIT\\n\", saveStateToDisk.c_str());\n                exit(1);\n            }\n        }\n    } else {\n        ROS_WARN( \"[cerebro_node] saveStateToDisk cmdline parameter was not found, so I will not saveStateToDisk()\");\n    }\n\n    //--- END loadStateFromDisk, saveStateToDisk ---//\n\n\n\n    //--- Config File ---//\n    string config_file;\n    if( !nh.getParam( \"config_file\", config_file ) )\n    {\n        ROS_ERROR( \"[cerebro_node] config_file cmdline parameter required to read the camera matrix.\\nThis is fatal error\\n..quit...\" );\n        exit(1);\n    }\n    ROS_WARN( \"Config File Name : %s\", config_file.c_str() );\n\n    // check if file exists - depends on boost\n    if ( !boost::filesystem::exists( config_file ) )\n    {\n      std::cout << \"[cerebro_node] Can't find my file: \" << config_file << std::endl;\n      ROS_ERROR_STREAM( \"[cerebro_node] Can't find config_file:\" << config_file );\n      exit(1);\n    }\n\n    cv::FileStorage fs(config_file, cv::FileStorage::READ);\n    if( !fs.isOpened() )\n    {\n        ROS_ERROR_STREAM( \"[cerebro_node] cannot open config_file: \"<< config_file << \"\\nThe file seems to be stated on the cmdline but it cannot be opened, possibly file is not found on the stated path or it does not have read permission\\n...fatal...quit()\\n\" );\n        exit(1);\n    }\n\n\n\n    // --- Get yaml config for primary camera. (cam0) ---//\n    camodocal::CameraPtr abstract_camera;\n    // abstract_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(config_file);\n    // assert( abstract_camera && \"Even after loading yaml the camera seem invalid. Something wrong\\n\");\n    {\n        string cam0_calib;\n        if( !fs[\"cam0_calib\"].isString() )\n        {\n            ROS_WARN_STREAM( \"[cerebro_node] cannot find key `cam0_calib` in config_file=\" << config_file  );\n        }\n        else {\n            fs[\"cam0_calib\"] >> cam0_calib;\n            ROS_INFO(  \"cam0_calib : %s\", cam0_calib.c_str() );\n\n            vector<string> ___path = MiscUtils::split( config_file, '/' );\n            // does::> $(python) '/'.join( ___path[0:-1] )\n            string ___cam0_path = string(\"\");\n            for( int _i = 0 ; _i<___path.size()-1 ; _i++ ) {\n                ___cam0_path += ___path[_i] + \"/\";\n            }\n            ___cam0_path += \"/\"+cam0_calib;\n            cout << \"cam0_fullpath=\" << ___cam0_path << endl;\n\n\n            // check if file exists - depends on boost\n            if ( !boost::filesystem::exists( ___cam0_path ) )\n            {\n              std::cout << \"[cerebro_node] Can't find my file for primary camera: \" << ___cam0_path << std::endl;\n              ROS_ERROR_STREAM( \"[cerebro_node] Can't find my file for primary camera\" << ___cam0_path );\n              exit(1);\n            }\n\n            // Make Abstract Camera\n            // camodocal::CameraPtr abstract_camera_1;\n            cout << TermColor::GREEN() << \"Load file : \" << ___cam0_path << TermColor::RESET() << endl;\n            abstract_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(___cam0_path);\n            assert( abstract_camera && \"Even after loading yaml the camera seem invalid. Something wrong\\n\");\n\n\n            // dataManager.setAbstractCamera( abstract_camera_1, 1 );\n        }\n    }\n\n\n\n\n    // PinholeCamera camera = PinholeCamera( config_file );\n    // camera.printCameraInfo(2); // Not in use, using camodocal camera instead.\n\n\n    //--- DataManager ---//\n    DataManager dataManager = DataManager(nh);\n    // dataManager.setCamera(camera);\n    dataManager.setAbstractCamera( abstract_camera );\n\n\n\n    //--- Subscribers ---// TODO: move all subscribers this to dataManager class ?\n\n    // [A]\n    // w_T_c: Pose of camera (all, only a subset are keyframes) in world-cordinate system.\n    string camera_pose_topic = string(\"/vins_estimator/camera_pose\");\n    ROS_INFO( \"Subscribe to camera_pose_topic: %s\", camera_pose_topic.c_str() );\n    ros::Subscriber sub_odometry = nh.subscribe( camera_pose_topic, 1000, &DataManager::camera_pose_callback, &dataManager );\n\n\n    // // [A.1] TODO: not needed. consider not subscribing\n    // // Marker for keyframes. Caution, these poses are of imu (NOT of camera).\n    // string keyframe_pose_topic = string(\"/vins_estimator/keyframe_pose\");\n    // ROS_INFO( \"Subscribe to keyframe_camera_pose_topic: %s\", keyframe_pose_topic.c_str() );\n    // ros::Subscriber sub_kf_odometry = nh.subscribe( keyframe_pose_topic, 1000, &DataManager::keyframe_pose_callback, &dataManager );\n\n\n\n    // [B]\n    // Raw Image (all). The topic's name is in the config_file\n\n    string raw_image_topic;\n    if( !fs[\"image0_topic\"].isString() )\n    {\n        ROS_ERROR_STREAM( \"[cerebro_node] cannot find key 'image0_topic' in config_file(\"<< config_file << \")\\n...fatal...quit\" );\n        exit(1);\n    }\n    fs[\"image0_topic\"] >> raw_image_topic;\n    ROS_INFO( \"Subscribe to raw_image_topic: %s\", raw_image_topic.c_str() );\n    ros::Subscriber sub_image = nh.subscribe( raw_image_topic, 10, &DataManager::raw_image_callback, &dataManager );\n\n    // [B.1]\n    // Additional Image topic (stereo pair)\n    string raw_image_topic_1;\n    ros::Subscriber sub_image_1;\n    if( !fs[\"image1_topic\"].isString() )\n    {\n        ROS_WARN_STREAM( \"[cerebro_node] cannot find key `image1_topic` in config_file=\" << config_file << \". This was optional so nothing to worry if you don't need the stereo pair\" );\n        exit(1);\n    }\n    else {\n        fs[\"image1_topic\"] >> raw_image_topic_1;\n        ROS_INFO( \"Subscribe to image_topic_1: %s\", raw_image_topic_1.c_str() );\n        sub_image_1 = nh.subscribe( raw_image_topic_1, 10, &DataManager::raw_image_callback_1, &dataManager );\n    }\n\n\n    // [B.2]\n    // Additional Image topic (realsense depth) 16UC1\n    string depth_image_topic = \"/camera/depth/image_rect_raw\";\n    ros::Subscriber sub_depth_image;\n    ROS_INFO( \"******\\nSubscribe to depth_image_topic: %s\\n********\", depth_image_topic.c_str() );\n    sub_depth_image = nh.subscribe( depth_image_topic, 10, &DataManager::depth_image_callback, &dataManager );\n\n\n\n\n\n    // [B.2]\n    // Additional Cameras (yaml)\n    string camera_yaml_1;\n    if( !fs[\"cam1_calib\"].isString() )\n    {\n        ROS_WARN_STREAM( \"[cerebro_node] cannot find key `cam1_calib` in config_file=\" << config_file << \". This was optional so nothing to worry if you don't need the 2nd camera\" );\n        exit(1);\n    }\n    else {\n        fs[\"cam1_calib\"] >> camera_yaml_1;\n        ROS_INFO(  \"cam1_calib : %s\", camera_yaml_1.c_str() );\n\n        vector<string> ___path = MiscUtils::split( config_file, '/' );\n        // does::> $(python) '/'.join( ___path[0:-1] )\n        string ___camera_yaml_1_path = string(\"\");\n        for( int _i = 0 ; _i<___path.size()-1 ; _i++ ) {\n            ___camera_yaml_1_path += ___path[_i] + \"/\";\n        }\n        ___camera_yaml_1_path += \"/\"+camera_yaml_1;\n        cout << \"camera_yaml_1_fullpath=\" << ___camera_yaml_1_path << endl;\n\n\n        // check if file exists - depends on boost\n        if ( !boost::filesystem::exists( ___camera_yaml_1_path ) )\n        {\n          std::cout << \"[cerebro_node] Can't find my file for additional camera: \" << ___camera_yaml_1_path << std::endl;\n          ROS_ERROR_STREAM( \"[cerebro_node] Can't find my file for additional camera\" << ___camera_yaml_1_path );\n          exit(1);\n        }\n\n        // Make Abstract Camera\n        camodocal::CameraPtr abstract_camera_1;\n        cout << TermColor::GREEN() << \"Load file : \" << ___camera_yaml_1_path << TermColor::RESET() << endl;\n        abstract_camera_1 = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(___camera_yaml_1_path);\n        assert( abstract_camera_1 && \"Even after loading yaml the camera_1 seem invalid. Something wrong\\n\");\n\n        dataManager.setAbstractCamera( abstract_camera_1, 1 );\n    }\n\n\n    // [B.3]\n    // Camera baseline. Set stereobaseline transform ie. right_T_left from yaml file\n    if(  !fs[\"extrinsic_1_T_0\"].isString() )\n    {\n        ROS_WARN_STREAM( \"[cerebro_node] cannot find key `extrinsic_1_T_0` in config file=\" << config_file << \" this was not needed for monocular camera, but needed for a stereo camera\" );\n        cout << TermColor::RED() <<  \"[cerebro_node] cannot find key `extrinsic_1_T_0` in config file=\" << config_file << \" this was not needed for monocular camera, but needed for a stereo camera\" << TermColor::RESET() << endl;\n        // exit(1);\n\n\n        #if 1\n        // look for the existence of `body_T_cam0` and `body_T_cam1`\n        cout << TermColor::YELLOW() << \"Since, the key `extrinsic_1_T_0`, I am looking for the existence of `body_T_cam0` and `body_T_cam1`\" << TermColor::RESET() << endl;\n\n        if( fs[\"body_T_cam0\"].empty() || fs[\"body_T_cam1\"].empty() ) {\n            cout << TermColor::RED() << \"Either of the keys `body_T_cam0` or `body_T_cam1` cannot be found in config file. FATAL ERROR\\n\" << TermColor::RESET() ;\n            exit(1);\n        }\n\n\n        cout << TermColor::iGREEN() <<  \"The keys `body_T_cam0` and `body_T_cam1` exists, compute extrinsic_1_T_0 from these two as ` ee_body_T_cam1.inverse() * ee_body_T_cam0;`\\n\" << TermColor::RESET() ;\n        cv::Mat body_T_cam0, body_T_cam1;\n        fs[\"body_T_cam0\"] >> body_T_cam0;\n        fs[\"body_T_cam1\"] >> body_T_cam1;\n        Matrix4d ee_body_T_cam0, ee_body_T_cam1;\n        cv::cv2eigen( body_T_cam0,  ee_body_T_cam0 );\n        cv::cv2eigen( body_T_cam1,  ee_body_T_cam1 );\n\n        Matrix4d cam1_T_cam0 = ee_body_T_cam1.inverse() * ee_body_T_cam0;\n        cout << \"Matrix4d cam1_T_cam0 = \" << PoseManipUtils::prettyprintMatrix4d(cam1_T_cam0) << endl;\n        {\n        cout << \"raw matrix of cam1_T_cam0:\\n\" << cam1_T_cam0 << \"----\" <<  endl;\n\n        double q_xyzw[5], t_xyz[5];\n        PoseManipUtils::eigenmat_to_raw_xyzw( cam1_T_cam0, q_xyzw, t_xyz );\n        cout << \"q_xyzw: \" << q_xyzw[0] << \",\" << q_xyzw[1] << \",\" << q_xyzw[2] << \",\" << q_xyzw[3] << endl;\n        cout << \" t_xyz: \" << t_xyz[0] << \",\" << t_xyz[1] << \",\" << t_xyz[2]  << endl;\n        }\n        dataManager.setCameraRelPose( cam1_T_cam0, std::make_pair(1,0) );\n\n        #endif\n\n    }\n    else {\n        // Extract the fname from the config_file\n        string extrinsic_1_T_0;\n        fs[\"extrinsic_1_T_0\"] >> extrinsic_1_T_0;\n        ROS_INFO(  \"in config_file=%s; extrinsic_1_T_0 : %s\", config_file.c_str(), extrinsic_1_T_0.c_str() );\n\n        // Make full path from fname\n        vector<string> ___path = MiscUtils::split( config_file, '/' );\n        // does::> $(python) '/'.join( ___path[0:-1] )\n        string ___extrinsic_1_T_0_path = string(\"\");\n        for( int _i = 0 ; _i<___path.size()-1 ; _i++ ) {\n            ___extrinsic_1_T_0_path += ___path[_i] + \"/\";\n        }\n        ___extrinsic_1_T_0_path += \"/\"+extrinsic_1_T_0;\n        cout << \"___extrinsic_1_T_0_fullpath=\" << ___extrinsic_1_T_0_path << endl;\n\n\n        // Open fullpath of extrinsic.yaml\n        cout << \"opencv yaml reading: open file: \" << ___extrinsic_1_T_0_path << endl;\n        cv::FileStorage fs(___extrinsic_1_T_0_path, cv::FileStorage::READ);\n\n        if (!fs.isOpened())\n        {\n            ROS_ERROR_STREAM(  \"config_file asked to open extrinsicbasline file but it cannot be opened.\\nTHIS IS FATAL, QUITING\" );\n            exit(1);\n        }\n\n        cout << TermColor::GREEN() << \"successfully opened file \"<< ___extrinsic_1_T_0_path << TermColor::RESET() << endl;\n        cv::FileNode n = fs[\"transform\"];\n        if( n.empty() ) {\n            cout << TermColor::RED() << \"I was looking for the key `transform` in the file but it doesnt seem to exist. FATAL ERROR\" << TermColor::RESET() << endl;\n            exit(1);\n        }\n        Vector4d q_xyzw;\n        q_xyzw << (double)n[\"q_x\"] , (double)n[\"q_y\"] ,(double) n[\"q_z\"] ,(double) n[\"q_w\"];\n\n        Vector3d tr_xyz;\n        tr_xyz << (double)n[\"t_x\"] , (double)n[\"t_y\"] ,(double) n[\"t_z\"];\n\n        cout << \"--values from file--\\n\" << TermColor::iGREEN();\n        cout << \"q_xyzw:\\n\" << q_xyzw << endl;\n        cout << \"tr_xyz:\\n\" << tr_xyz << endl;\n        cout << TermColor::RESET() << endl;\n\n        Matrix4d _1_T_0;\n        PoseManipUtils::raw_xyzw_to_eigenmat( q_xyzw, tr_xyz/1000., _1_T_0 ); cout << \"translation divided by 1000 to convert from mm (in file) to meters (as needed)\\n\";\n        // cout << TermColor::iBLUE() << \"_1_T_0:\\n\" <<  _1_T_0  << TermColor::RESET() << endl;\n        cout << TermColor::iBLUE() << \"_1_T_0: \" << PoseManipUtils::prettyprintMatrix4d( _1_T_0 ) << TermColor::RESET() << endl;\n        cout << \"_1_T_0:\\n\" << _1_T_0 << endl;\n\n\n        dataManager.setCameraRelPose( _1_T_0, std::make_pair(1,0) );\n\n\n        #if 0\n        // verify if it was correctly set --> Works! Verified !\n        cout << \"isCameraRelPoseSet ? \" << dataManager.isCameraRelPoseSet( std::make_pair(1,0) ) << endl;\n        cout << \"isCameraRelPoseSet ? \" << dataManager.isCameraRelPoseSet( std::make_pair(6,10) ) << endl;\n        // cout << \"getCameraRelPose:\\n\" << dataManager.getCameraRelPose( std::make_pair(1,0) ) << endl;\n        cout << \"getCameraRelPose:\\n\" << PoseManipUtils::prettyprintMatrix4d( dataManager.getCameraRelPose( std::make_pair(1,0) ) ) << endl;\n        auto ___all_available_keys = dataManager.getCameraRelPoseKeys();\n        cout << \"# relative poses available = \" << ___all_available_keys.size() << endl;\n        #endif\n    }\n\n\n\n    // [C]\n    // imu_T_cam : imu camera extrinsic calib. Will store this just in case there is a need\n    string extrinsic_cam_imu_topic = string(\"/vins_estimator/extrinsic\");\n    ROS_INFO( \"Subscribe to extrinsic_cam_imu_topic: %s\", extrinsic_cam_imu_topic.c_str() );\n    ros::Subscriber sub_extrinsic = nh.subscribe( extrinsic_cam_imu_topic, 1000, &DataManager::extrinsic_cam_imu_callback, &dataManager );\n\n\n    // [D]\n    // PointCloud (all): has 5 channels\n    string ptcld_topic = string(\"/vins_estimator/keyframe_point\");\n    // string ptcld_topic = string(\"/feature_tracker/feature\");\n    ROS_INFO( \"Subscribe to ptcld_topic: %s\", ptcld_topic.c_str() );\n    ros::Subscriber sub_ptcld = nh.subscribe( ptcld_topic, 1000, &DataManager::ptcld_callback, &dataManager );\n\n    // [E]\n    // Tracked features. these contains a) unvn in msg->points b) chnl[0] id of the point c) chnl[1], chnl[2] := (u,v) d) chnl[3], chnl[4] not very sure.\n    // string tracked_feat_topic = string(\"/feature_tracker/feature\");\n    // ROS_INFO( \"Subscribe to tracked_feat_topic: %s\", tracked_feat_topic.c_str() );\n    // ros::Subscriber sub_tracked_feat = nh.subscribe( tracked_feat_topic, 1000, &DataManager::tracked_feat_callback, &dataManager );\n\n\n\n    // set this to 1 to enable loading state, set this to 0 to not load state\n    // If you dont loadStateFromDisk, make sure you initialize the ImageDataManager.\n    #define __LOAD_STATE__ 0\n    #if __LOAD_STATE__\n    //--- Load State From Disk\n    dataManager.loadStateFromDisk( \"/Bulk_Data/chkpts_cerebro\" );\n    // cout << \"PREMATURE EXIT\\n\";\n    // exit(1);\n    #else\n    dataManager.getImageManagerRef()->initStashDir(true); // this will use the default /tmp/cerebro_stash as the stashing directory\n    #endif\n\n\n\n    // If you dont loadStateFromDisk, make sure you initialize the ImageDataManager.\n    if( loadStateFromDisk.compare(\"\") != 0 )\n    {\n        dataManager.loadStateFromDisk( loadStateFromDisk );\n    }\n    else\n    {   //empty, fresh start\n        dataManager.getImageManagerRef()->initStashDir(true); // this will use the default /tmp/cerebro_stash as the stashing directory\n    }\n\n\n    //--- Start Threads ---//\n\n    // [A]\n    // Data associate thread: looks at the callback buffers and sets the data in the std::map\n    dataManager.data_association_thread_enable();\n    // dataManager.data_association_thread_disable();\n    std::thread data_association_th( &DataManager::data_association_thread, &dataManager, 15 );\n\n    dataManager.trial_thread_enable();\n    dataManager.trial_thread_disable();\n    std::thread dm_trial_th( &DataManager::trial_thread, &dataManager ); //< this threads prints datamanagers status to /dev/pts/20 modify as need be.\n\n    // [A.1] Another thread in class dataManager which will deallocate images in nonkeyframes.\n    dataManager.clean_up_useless_images_thread_enable();\n    // dataManager.clean_up_useless_images_thread_disable();\n    std::thread dm_cleanup_th( &DataManager::clean_up_useless_images_thread, &dataManager );\n\n\n    Cerebro cer( nh );\n    cer.setDataManager( &dataManager );\n    cer.setPublishers( \"/cerebro\" );\n\n\n\n\n    // [B.00]\n    // Kidnap Message Publisher. See also comments below for `kidnap message subscriber`\n    // Setup Publisher for sending kidnaped message to\n    // a) vins_estimator and b) pose_graph solver\n    string pub_topic_test = \"/feature_tracker/rcvd_flag\";\n    ROS_INFO( \"main : Publisher pub_topic_test: %s\", pub_topic_test.c_str() );\n    ros::Publisher rcvd_flag_pub = nh.advertise<std_msgs::Bool>(pub_topic_test, 1000);\n    // We publish std_msgs::Header to the pose-graph-solver.\n    // The purpose is that, this will serve as carrier of timestamp according to which\n    // we can eliminate the odometry edges from the cost function.\n    string pub_topic_header = \"/feature_tracker/rcvd_flag_header\";\n    ROS_INFO( \"main : Publisher pub_topic_header: %s\", pub_topic_header.c_str() );\n    ros::Publisher kidnap_indicator_header_pub = nh.advertise<std_msgs::Header>(pub_topic_header, 1000);\n    dataManager.setKidnapIndicatorPublishers( rcvd_flag_pub, kidnap_indicator_header_pub );\n\n\n    // [B.01]\n    // Kidnap message subscriber. There are two kinds of kidnap messages\n    // a. Bool b. Header. Look at the documentation of the kidnaped thread to know the details.\n    // This has been done only for compatibility. I (mpkuse) prefer the Header because it\n    // can also contain the timestamp when kidnap started and ended.\n    string kidnap_bool_topic = \"/feature_tracker/rcvd_flag\";\n    ROS_INFO( \"Subscribe to kidnap_bool_topic: %s\", kidnap_bool_topic.c_str() );\n    ros::Subscriber sub_kidnap_bool = nh.subscribe( kidnap_bool_topic, 1000, &Cerebro::kidnap_bool_callback, &cer );\n\n    string kidnap_header_topic = \"/feature_tracker/rcvd_flag_header\";\n    ROS_INFO( \"Subscribe to kidnap_header_topic: %s\", kidnap_header_topic.c_str() );\n    ros::Subscriber sub_kidnap_header = nh.subscribe( kidnap_header_topic, 1000, &Cerebro::kidnap_header_callback, &cer );\n\n\n    // [B]\n    // Descriptor Computation Thread.\n    //      It monitors data_map. If new keyframes are available then\n    //      it queries the ros-service with the image to get the whole-image-descriptor.\n    //      This whole image descriptor is stored in `node->setWholeImageDescriptor`\n    //      and the index of computation stored in `wholeImageComputedList`.\n    cer.descriptor_computer_thread_enable();\n    // cer.descriptor_computer_thread_disable();\n    std::thread desc_th( &Cerebro::descriptor_computer_thread, &cer ); //runs @20hz\n\n\n    // [C]\n    // loopcandidates producer\n    //      This thread looks at `wholeImageComputedList`, if there is something new\n    //      in it (new descriptors) it does dot(  0-->T-50, T ), dot(  0-->T-50, T-1 )\n    //      and dot(  0-->T-50, T-2 ). If threshold-test and locality-test both\n    //      comeout to be +ve it declares 'loop found' and queues up this pair into the\n    //      list `foundLoops`\n    cer.run_thread_enable();\n    // cer.run_thread_disable();\n    std::thread dot_product_th( &Cerebro::run, &cer ); //< descrip_N__dot__descrip_0_N. runs @ 10hz\n\n\n    // [C.1]\n    // loopcandidates consumer\n    //      Monitors the list `foundLoops` aka the putative loop candidates.\n    //      If new candidates are present in the list it computes the relative-pose\n    //      using GMSMatcher and theia-sfm's pnp. The depth is computed from stereogeom (class StereoGeometry)\n    cer.loopcandidate_consumer_enable();\n    // cer.loopcandidate_consumer_disable();\n    std::thread loopcandidate_consumer_th( &Cerebro::loopcandiate_consumer_thread, &cer ); // runs @1hz\n\n    // [D]\n    // Kidnap Identification Thread\n    cer.kidnaped_thread_enable();\n    // cer.kidnaped_thread_disable();\n    std::thread kidnap_th( &Cerebro::kidnaped_thread, &cer, 5 );\n\n    // [E]\n    // Visualization -\n    Visualization viz(nh);\n    viz.setDataManager( &dataManager );\n    viz.setCerebro( &cer );\n    viz.setVizPublishers( \"/cerebro_node/viz/\" );\n    viz.run_thread_enable();\n    // viz.run_thread_disable();\n    std::thread viz_th( &Visualization::run, &viz, 10 ); //TODO something wrong with the logic in publish. another solution could be we keep #seq in DataNode.\n\n\n    fs.release();\n\n    ros::spin();\n\n    // seem like user has pressed CTRL+C, so, stop all threads.\n    dataManager.data_association_thread_disable();\n    dataManager.trial_thread_disable();\n    dataManager.clean_up_useless_images_thread_disable();\n    #if 1\n    cer.run_thread_disable();\n    cer.descriptor_computer_thread_disable();\n    cer.loopcandidate_consumer_disable();\n    cer.kidnaped_thread_disable();\n    viz.run_thread_disable();\n    #endif\n\n    data_association_th.join(); cout << \"data_association_th.join()\\n\";\n    dm_trial_th.join(); cout << \"t1_trial.join()\\n\";\n    dm_cleanup_th.join(); cout << \"dm_cleanup_th.join()\\n\";\n\n    #if 1\n    dot_product_th.join(); cout << \"dot_product_th.join()\\n\";\n    desc_th.join(); cout << \"desc_th.join()\\n\";\n    loopcandidate_consumer_th.join(); cout << \"loopcandidate_consumer_th.join()\\n\";\n    kidnap_th.join(); cout << \"kidnap_th.join()\\n\";\n    viz_th.join(); cout << \"viz_th.join()\\n\";\n    #endif\n\n\n\n    //make this to 1 to save state to file upon exit\n    #define __SAVE_STATE__ 0\n    #if __SAVE_STATE__\n    // Save State (for relocalization)\n    dataManager.saveStateToDisk( \"/Bulk_Data/chkpts_cerebro\" );\n    #endif\n\n    if( saveStateToDisk.compare(\"\") != 0 )\n    {\n        dataManager.saveStateToDisk( saveStateToDisk );\n    }\n\n\n\n    ///////////////////////////////////////////////////\n    // A demo of how to look inside dataManager.     //\n    ///////////////////////////////////////////////////\n    #if 0\n    {\n        std::map< ros::Time, DataNode* > data_map = dataManager.getDataMapRef();\n        // auto descriptor_map = cer.getDescriptorMapRef();\n        for( auto it = data_map.begin() ; it!= data_map.end() ; it++ )\n        {\n            cout << TermColor::RED() << \"---\" << TermColor::RESET() << endl;\n            cout << \"Map-Key: \" << it->first << \"\\tseq=\" << std::distance( data_map.begin(), it ) << \"\\t\" << it->first - dataManager.getPose0Stamp() << endl;\n            cout << \"Map-Value:\\n\";\n            it->second->prettyPrint(  );\n\n            if( false && it->second->isImageAvailable() ) {\n                cv::imshow( \"win\", it->second->getImage() );\n                cv::waitKey(30);\n            }\n        }\n\n        // Printing Global Variables\n        cout << \"Pose0 : isAvailable=\" << dataManager.isPose0Available() << \"\\t\";\n        cout << \"stamp=\" << dataManager.getPose0Stamp() ;\n        cout << endl;\n\n        cout << \"Camera:\\n\" ;\n        dataManager.getCameraRef().printCameraInfo(2);\n\n        cout << \"IMUCamExtrinsic : isAvailable=\" << dataManager.isIMUCamExtrinsicAvailable() << \"\\t\";\n        cout << \"last updated : \" << dataManager.getIMUCamExtrinsicLastUpdated() << \"\\t\" << dataManager.getIMUCamExtrinsicLastUpdated() -dataManager.getPose0Stamp() ;\n        cout << \"\\nimu_T_cam : \\n\" << PoseManipUtils::prettyprintMatrix4d( dataManager.getIMUCamExtrinsic() ) << endl;\n    }\n    #endif\n\n\n\n\n\n    ///////////////////////\n    // Actual Logging.  //\n    //////////////////////\n    #define __LOGGING__ 0 // make this 1 to enable logging. 0 to disable logging. rememeber to catkin_make after this change\n    #if __LOGGING__\n    // Note: If using roslaunch to launch this node and when LOGGING is enabled,\n    // roslaunch sends a sigterm and kills this thread when ros::ok() returns false ie.\n    // when you press CTRL+C. The timeout is governed by roslaunch.\n    //\n    // If you wish to increase this timeout, you need to edit \"/opt/ros/kinetic/lib/python2.7/dist-packages/roslaunch/nodeprocess.py\"\n    // then edit these two vars.\n    // _TIMEOUT_SIGINT = 15.0 #seconds\n    // _TIMEOUT_SIGTERM = 2.0 #seconds\n    //          ^^^^ Borrowed from : https://answers.ros.org/question/11353/how-to-delay-escalation-to-sig-term-after-sending-sig-int-to-roslaunch/\n\n        // Write json log\n        // string save_dir = \"/Bulk_Data/_tmp/\";\n        string save_dir = \"/tmp/_tmp/\";\n\n        ROS_INFO( \"cerebro_node logging to : %s.\\nThis will take upto 2 minutes.\", save_dir.c_str());\n        ROS_ERROR( \"cerebro_node logging to : %s.\\nThis will take upto 2 minutes.\", save_dir.c_str());\n        ROS_WARN( \"cerebro_node logging to : %s.\\nThis will take upto 2 minutes.\", save_dir.c_str());\n\n        //\n        // $ rm -rf ${save_dir}\n        // $ mkdir ${save_dir}\n        #include <cstdlib>\n        string system_cmd;\n\n        system_cmd = string(\"rm -rf \"+save_dir).c_str();\n        // cout << TermColor::YELLOW() << system_cmd << TermColor::RESET() << endl;\n        // int rm_dir_err = system( system_cmd.c_str() );\n        const int rm_dir_err = RawFileIO::exec_cmd( system_cmd );\n\n        if ( rm_dir_err == -1 )\n        {\n            cout << TermColor::RED() << \"[cerebro_node] Error removing directory!\\n\" << TermColor::RESET() << endl;\n            exit(1);\n        }\n\n        system_cmd = string(\"mkdir -p \"+save_dir).c_str();\n        // cout << TermColor::YELLOW() << system_cmd << TermColor::RESET() << endl;\n        // const int dir_err = system( system_cmd.c_str() );\n        const int dir_err = RawFileIO::exec_cmd( system_cmd );\n        if ( dir_err == -1 )\n        {\n            cout << TermColor::RED() << \"[cerebro_node] Error creating directory!\\n\" << TermColor::RESET() << endl;\n            exit(1);\n        }\n        // done emptying the directory.\n\n\n        RawFileIO::write_string( save_dir+\"/log.json\", dataManager.asJson().dump(4) );\n        // RawFileIO::write_string( save_dir+\"/log.json\", dataManager.metaDataAsJson() );\n        // RawFileIO::write_string( save_dir+\"/log.txt\", dataManager.metaDataAsFlatFile() ); //TODO remove. Since i can read json in python as well as c++ with ease, these is no point of storing stuff as txt\n        // return 0;\n\n        #if 1\n        // std::map< ros::Time, DataNode* > data_map = dataManager.getDataMapRef(); //old - can remove\n        auto data_map = dataManager.getDataMapRef();\n        auto img_data_mgr = dataManager.getImageManagerRef();\n        for( auto it = data_map->begin() ; it!= data_map->end() ; it++ )\n        {\n            int seq_id = std::distance( data_map->begin() , it );\n\n            string fname = save_dir+\"/\"+to_string(seq_id );\n            #if 0\n            //old code where we used images inside Nodes.  - mark for removal\n            if( it->second->isImageAvailable() )\n                RawFileIO::write_image( fname+\".jpg\", it->second->getImage()  );\n\n            // additional image\n            if( it->second->isImageAvailable(1) )\n                RawFileIO::write_image( fname+\"_1.jpg\", it->second->getImage(1)  );\n\n            #else\n            if( img_data_mgr->isImageRetrivable( \"left_image\", it->first  ) )\n            {\n                cv::Mat outimg;\n                img_data_mgr->getImage( \"left_image\", it->first, outimg );\n                RawFileIO::write_image( fname+\".jpg\", outimg  );\n            }\n\n            if( img_data_mgr->isImageRetrivable( \"right_image\", it->first  ) )\n            {\n                cv::Mat outimg;\n                img_data_mgr->getImage( \"right_image\", it->first, outimg );\n                RawFileIO::write_image( fname+\"_1.jpg\", outimg  );\n            }\n            #endif\n\n\n\n            // Save VINS pose\n            if( it->second->isPoseAvailable() ) {\n                RawFileIO::write_EigenMatrix( fname+\".wTc\", it->second->getPose() );\n            }\n\n            // Save Point Cloud\n            #if 0\n            if( it->second->isPtCldAvailable() ) {\n                RawFileIO::write_EigenMatrix( fname+\".wX.pointcloud\", it->second->getPointCloud() );\n                if( it->second->isPoseAvailable() ) {\n                    RawFileIO::write_EigenMatrix( fname+\".cX.pointcloud\", it->second->getPose().inverse() *  it->second->getPointCloud() );\n                }\n                else\n                    ROS_WARN( \"ptclod is available but pose is not available at seq_id=%d\", seq_id );\n            }\n            #endif\n\n\n            // Save Tracked Points\n            #if 0\n            if( it->second->isUVAvailable() ) {\n                RawFileIO::write_EigenMatrix( fname+\".unvn\", it->second->getUnVn() );\n                RawFileIO::write_EigenMatrix( fname+\".uv\", it->second->getUV() );\n                RawFileIO::write_EigenMatrix( fname+\".id\", it->second->getFeatIds() );\n            }\n\n            // Save Whole Image Descriptor\n            if( it->second->isWholeImageDescriptorAvailable() ) {\n                RawFileIO::write_EigenMatrix( fname+\".imgdesc\", it->second->getWholeImageDescriptor() );\n            }\n            #endif\n        }\n        #endif\n\n\n        // Save Camera Matrix and IMUCamExtrinsic\n        vector<short> cam_keys = dataManager.getAbstractCameraKeys();\n        for( short _icam=0 ; _icam < cam_keys.size() ; _icam++ )\n        {\n            short key=cam_keys[_icam];\n            if( dataManager.isAbstractCameraSet(key) ) {\n                auto abstract_camera = dataManager.getAbstractCameraRef(key);\n                RawFileIO::write_string( save_dir+\"/cameraIntrinsic.\"+std::to_string(key)+\".info\", abstract_camera->parametersToString() );\n                abstract_camera->writeParametersToYamlFile( save_dir+\"/cameraIntrinsic.\"+std::to_string(key)+\".yaml\" );\n            }\n        }\n\n\n        // Save Camera-IMU Extrinsics\n        cout << \"IMUCamExtrinsic : isAvailable=\" << dataManager.isIMUCamExtrinsicAvailable() << \"\\t\";\n        cout << \"last updated : \" << dataManager.getIMUCamExtrinsicLastUpdated() << \"\\t\" << dataManager.getIMUCamExtrinsicLastUpdated() -dataManager.getPose0Stamp() ;\n        cout << \"\\nimu_T_cam : \\n\" << PoseManipUtils::prettyprintMatrix4d( dataManager.getIMUCamExtrinsic() ) << endl;\n        if( dataManager.isIMUCamExtrinsicAvailable() ) {\n            RawFileIO::write_EigenMatrix( save_dir+\"/IMUCamExtrinsic.imu_T_cam\", dataManager.getIMUCamExtrinsic() );\n\n            std::stringstream buffer;\n            buffer << \"IMUCamExtrinsicLastUpdated: \"<< dataManager.getIMUCamExtrinsicLastUpdated() << endl;\n            buffer << \"IMUCamExtrinsicLastUpdated Rel: \"<< dataManager.getIMUCamExtrinsicLastUpdated() -dataManager.getPose0Stamp()  << endl;\n            RawFileIO::write_string( save_dir+\"/IMUCamExtrinsic.info\", buffer.str() );\n        }\n        else {\n            ROS_ERROR( \"[cerebro_node.cpp] IMUCamExtrinsic appear to be not set...something seem wrong\\n\" );\n        }\n\n\n        // Save foundLoops from Cerebro class\n        json jsonout_obj = cer.foundLoops_as_JSON();\n        RawFileIO::write_string( save_dir+\"/loopcandidates_liverun.json\", jsonout_obj.dump(4) );\n\n        // Save kidnap info\n        json json_kidnap_info = cer.kidnap_info_as_json();\n        RawFileIO::write_string( save_dir+\"/kidnap_info.json\", json_kidnap_info.dump(4) );\n\n\n\n        // Save matching images from ProcessedLoopCandidate\n        string cmd_ = string(\"mkdir -p \")+save_dir+\"/matching/\";\n        RawFileIO::exec_cmd( cmd_ );\n\n\n        //******\n        // If you are looking to ***save*** these matchings image pairs\n        // besure to set the #define just before `Cerebro::loopcandiate_consumer_thread()`.\n\n        json all_procloops_json_obj;\n        for( int i=0 ; i<cer.processedLoops_count() ; i++ ) {\n\n            // Retrive i^{th} item\n            ProcessedLoopCandidate c = cer.processedLoops_i( i );\n\n            // if( c.isSet_3d2d__2T1 == false ) {\n                // cout << \"ignore ProcessedLoopCandidate[\" << i << \"] because c.isSet_3d2d__2T1 == false. aka poses computed from multiple methods didnt seem consistent\\n\";\n                // continue;\n            // }\n\n            // Write all the logged images\n            for( int l=0 ; l<c.debug_images.size() ; l++ ) {\n                cv::Mat __imm = c.debug_images[l] ;\n                if( __imm.rows > 0 && __imm.cols > 0 ) {\n                    RawFileIO::write_image( save_dir+\"/matching/im_pair_\"+to_string(i)+\"_\"+c.debug_images_titles[l]+\".jpg\", __imm );\n                }\n                else {\n                    cout << \"in logging, matching_im_pair of ProcessedLoopCandidate[\" << i << \"] is not available\\n\";\n                }\n            }\n\n\n            // Json of the object\n            json procloops_json_obj;\n            if( c.asJson(procloops_json_obj) ) {\n                procloops_json_obj[\"processedLoops_i\"] = i;\n                all_procloops_json_obj.push_back( procloops_json_obj );\n            }\n            else {\n                cout << \"cannot make json for this  ProcessedLoopCandidate[\" << i << \"]\\n\";\n            }\n\n            // Write the final poses as files\n            if( c.isSet_3d2d__2T1 )\n                RawFileIO::write_EigenMatrix( save_dir+\"/matching/im_pair_\"+to_string(i)+\".3d2d__2T1\", c._3d2d__2T1 );\n\n            // Write final pose status image.\n            string __final_pose_status_im_string = \"\";\n            if( c.isSet_3d2d__2T1 ) {\n                __final_pose_status_im_string = \"TRUE (poses were consistent);\";\n                __final_pose_status_im_string += PoseManipUtils::prettyprintMatrix4d( c._3d2d__2T1 );\n            } else {\n                __final_pose_status_im_string = \"FALSE (non consistent poses);\";\n            }\n            cv::Mat __final_pose_status_im = cv::Mat::zeros(cv::Size(400, 20), CV_8UC3);\n            MiscUtils::append_status_image( __final_pose_status_im,  __final_pose_status_im_string );\n            RawFileIO::write_image( save_dir+\"/matching/im_pair_\"+to_string(i)+\"_aaconsistency_status.jpg\", __final_pose_status_im );\n\n        }\n        RawFileIO::write_string( save_dir+\"/matching/ProcessedLoopCandidate.json\", all_procloops_json_obj.dump(4) );\n\n    #endif\n\n\n\n\n    return 0 ;\n\n}\n"
  },
  {
    "path": "src/unittest/unittest_camera_geom_class_usage.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\n#include <iostream>\n#include <string>\n\n#include \"camodocal/camera_models/Camera.h\"\n#include \"camodocal/camera_models/CameraFactory.h\"\n#include \"camodocal/camera_models/CataCamera.h\"\n#include \"camodocal/camera_models/EquidistantCamera.h\"\n\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n#include \"../utils/MiscUtils.h\"\n#include \"../utils/ElapsedTime.h\"\n#include \"../utils/PoseManipUtils.h\"\n#include \"../utils/TermColor.h\"\n#include \"../utils/CameraGeometry.h\"\n#include \"../utils/RawFileIO.h\"\n\n// #include \"gms_matcher.h\"\n\n#include <assert.h>\n\n#include <ceres/ceres.h>\nusing namespace ceres;\n\n\n// Monocular\nint monocular_demo()\n{\n    const std::string BASE = \"/Bulk_Data/_tmp/\";\n\n\n    // Abstract Camera\n    camodocal::CameraPtr m_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(BASE+\"/cameraIntrinsic.0.yaml\");\n    cout << m_camera->parametersToString() << endl;\n\n    // Init Monocular Geometry\n    MonoGeometry monogeom( m_camera );\n\n    // Raw Image - Image from camera\n    int frame_id = 23;\n    cv::Mat im_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_id)+\".jpg\" );\n\n    cv::Mat im_undistorted;\n    monogeom.do_image_undistortion( im_raw, im_undistorted );\n\n\n    cv::imshow( \"im_raw\", im_raw );\n    cv::imshow( \"im_undistorted\", im_undistorted );\n    cv::waitKey(0);\n}\n\n\n// Stereo\nint stereo_demo()\n{\n    const std::string BASE = \"/Bulk_Data/_tmp/\";\n\n    //--------- Intrinsics load\n    camodocal::CameraPtr left_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(BASE+\"/cameraIntrinsic.0.yaml\");\n    camodocal::CameraPtr right_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(BASE+\"/cameraIntrinsic.1.yaml\");\n    cout << left_camera->parametersToString() << endl;\n    cout << right_camera->parametersToString() << endl;\n\n    //----------- Stereo Base line load (alsoed called extrinsic calibration)\n        // mynt eye\n    // TODO function to load extrinsic from yaml file.\n    // Vector4d q_xyzw = Vector4d( -1.8252509868889259e-04,-1.6291774489779708e-03,-1.2462127842978489e-03,9.9999787970731446e-01 );\n    // Vector3d tr_xyz = Vector3d( -1.2075905420832895e+02/1000.,5.4110610639412482e-01/1000.,2.4484815673909591e-01/1000. );\n    Vector4d q_xyzw = Vector4d( -7.0955103716253032e-04,-1.5775578725333590e-03,-1.2732644461763854e-03,9.9999769332040711e-01 );\n    Vector3d tr_xyz = Vector3d( -1.2006984141573309e+02/1000.,3.3956264524978619e-01/1000.,-1.6784055634087214e-01/1000. );\n    Matrix4d right_T_left;\n    PoseManipUtils::raw_xyzw_to_eigenmat( q_xyzw, tr_xyz, right_T_left );\n\n\n    std::shared_ptr<StereoGeometry> stereogeom;\n    stereogeom = std::make_shared<StereoGeometry>( left_camera,right_camera,     right_T_left  );\n    stereogeom->set_K( 375.0, 375.0, 376.0, 240.0 );\n\n\n    int frame_id = 1005;\n    cout << \"READ IMAGE \" << frame_id << endl;\n    cv::Mat imleft_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_id)+\".jpg\", 0 );\n    cv::Mat imright_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_id)+\"_1.jpg\", 0 );\n\n    cout << \"imleft_raw: \" << MiscUtils::cvmat_info( imleft_raw ) << endl;\n    cout << \"imright_raw: \" << MiscUtils::cvmat_info( imright_raw ) << endl;\n\n\n    if( imleft_raw.empty() || imright_raw.empty() ) {\n        cout << \"imleft_raw is empty OR imright_raw is empty\\n\";\n        return 0;\n    }\n\n    // will get 3d points, stereo-rectified image, and disparity false colormap\n    MatrixXd _3dpts; //4xN\n    cv::Mat imleft_srectified, imright_srectified;\n    cv::Mat disparity_for_visualization;\n\n    ElapsedTime timer;\n    timer.tic();\n    stereogeom->get_srectifiedim_and_3dpoints_and_disparity_from_raw_images(imleft_raw, imright_raw,\n        imleft_srectified, imright_srectified,\n         _3dpts, disparity_for_visualization );\n    cout << timer.toc_milli() << \" (ms)!!  get_srectifiedim_and_3dpoints_and_disparity_from_raw_images\\n\";\n\n    cv::imshow( \"imleft_srectified\", imleft_srectified );\n    cv::imshow( \"imright_srectified\", imright_srectified );\n    cv::imshow( \"disparity_for_visualization\", disparity_for_visualization );\n\n    cv::waitKey(0);\n    return 0;\n\n}\n\n\n\n\nint main()\n{\n    cout << \"Gello Horld\\n\";\n    // monocular_demo();\n    stereo_demo();\n}\n"
  },
  {
    "path": "src/unittest/unittest_camodocal_proj.cpp",
    "content": "// A usage of my Stereo class.\n#include <iostream>\nusing namespace std;\n\n\n\n\n/** Experiments to project 3d points on images.\n*/\n\n#include <iostream>\n\n#include \"../utils/PoseManipUtils.h\"\n#include \"../utils/RawFileIO.h\"\n#include \"../utils/MiscUtils.h\"\n\n// #include \"PinholeCamera.h\"\n#include \"camodocal/camera_models/Camera.h\"\n#include \"camodocal/camera_models/CameraFactory.h\"\n\n// const string base_path = \"/Bulk_Data/_tmp_cerebro/tum_magistrale1/\";\nconst string base_path = \"/Bulk_Data/_tmp/\";\n\n// TODO Should return read status, ie. false if file-not-found\nbool load_i( int i, cv::Mat& im, Matrix4d& wTc, MatrixXd& uv, MatrixXd& cX )\n{\n    string fname = base_path+\"/\"+std::to_string( i );\n\n    // Image\n    im = cv::imread( fname+\".jpg\" );\n    // cv::imshow( \"win\", im );\n\n    // VINS-pose (odometry)\n    RawFileIO::read_eigen_matrix( fname+\".wTc\", wTc );\n\n    // 3D points - in camera cords\n    RawFileIO::read_eigen_matrix( fname+\".cX.pointcloud\", cX );\n\n    // 2D points - tracked points\n    RawFileIO::read_eigen_matrix( fname+\".uv\", uv );\n\n}\n\n// TODO Should return read status, ie. false if file-not-found\nbool load_i( int i, cv::Mat& im, Matrix4d& wTc, MatrixXd& uv, MatrixXd& cX, VectorXi& uv_ids )\n{\n    string fname = base_path+\"/\"+std::to_string( i );\n\n    // Image\n    im = cv::imread( fname+\".jpg\" );\n    // cv::imshow( \"win\", im );\n\n    // VINS-pose (odometry)\n    RawFileIO::read_eigen_matrix( fname+\".wTc\", wTc );\n\n    // 3D points - in camera cords\n    RawFileIO::read_eigen_matrix( fname+\".cX.pointcloud\", cX );\n\n    // 2D points - tracked points\n    RawFileIO::read_eigen_matrix( fname+\".uv\", uv );\n\n    // IDs\n    RawFileIO::read_eigen_matrix( fname+\".id\", uv_ids );\n\n}\n\nint main()\n{\n    //\n    // Load Camera - from yaml\n    std::string calib_file = base_path+\"/cameraIntrinsic.0.yaml\";\n    camodocal::CameraPtr m_camera;\n    m_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(calib_file);\n    std::cout << ((m_camera)?\"cam is initialized\":\"cam is not initiazed\") << std::endl; //this should print 'initialized'\n\n\n\n\n\n    // Load 1st\n    int _idx_1 = 1331;\n    cv::Mat im_1;\n    Matrix4d wTc_1;\n    MatrixXd uv_1, cX_1;\n    VectorXi uv_id_1;\n    load_i( _idx_1, im_1, wTc_1, uv_1, cX_1, uv_id_1 );\n    // cv::imshow( \"533\", im_1 );\n    {\n        cv::Mat dst;\n        MiscUtils::plot_point_sets( im_1, uv_1, dst, cv::Scalar(255,0,0), uv_id_1, \"self uv\" );\n        cv::imshow( std::to_string(_idx_1), dst );\n    }\n\n\n    // // Load 2nd\n    int _idx_2 = 839;\n    cv::Mat im_2;\n    Matrix4d wTc_2;\n    MatrixXd uv_2, cX_2;\n    VectorXi uv_id_2;\n    // load_i( 536, im_2, wTc_2, uv_2, cX_2, uv_id_2 );\n    // load_i( 887, im_2, wTc_2, uv_2, cX_2 );\n    load_i( _idx_2, im_2, wTc_2, uv_2, cX_2, uv_id_2 );\n    // cv::imshow( \"535\", im_2 );\n    {\n        cv::Mat dst;\n        MiscUtils::plot_point_sets( im_2, uv_2, dst, cv::Scalar(255,255,255), uv_id_2, \"self uv\" );\n        cv::imshow( std::to_string(_idx_2), dst );\n    }\n\n\n    // project 1st 3d points using camodocal\n    MatrixXd reprojected_uv_1 = MatrixXd::Zero( 2, cX_1.cols() );\n    for( int i=0 ; i<cX_1.cols() ; i++ )\n    {\n        Vector3d P = Vector3d( cX_1.col(i).topRows(3) );\n        cout << \"P.rows=\" << P.rows() << \"  P.cols()=\" << P.cols() << endl;\n        cout << P << endl;\n\n        Vector2d p;\n        m_camera->spaceToPlane( P, p );\n        cout << \"---\\n\" << p << endl;\n        reprojected_uv_1.col(i) = p;\n    }\n    cv::Mat dst;\n    // MiscUtils::plot_point_sets( im_1, uv_1, dst, cv::Scalar(255,0,0), uv_id_1, \"self uv in blue\" );\n    MiscUtils::plot_point_sets( im_2, reprojected_uv_1, dst, cv::Scalar(0,0,255), uv_id_1, \";self reproejct in red\" );\n    cv::imshow( \"reproejct\", dst );\n\n\n    cv::waitKey(0);\n    return 0;\n\n}\n"
  },
  {
    "path": "src/unittest/unittest_plot2mat.cpp",
    "content": "#include <iostream>\nusing namespace std;\n\n#include \"../utils/Plot2Mat.h\"\nint demo()\n{\n    cout << \"Hello\\n\";\n    Plot2Mat han;\n    han.setYminmax( -2, 2 );\n\n    for( int i=10; i<50; i++ ) {\n    VectorXd y = VectorXd::Random(10*i);\n    cout << \"y=\" << y.transpose() << endl;\n\n    han.mark( i );\n\n    han.plot(y);\n    cv::imshow(\"canvas\", han.getCanvasConstPtr() );\n    cv::waitKey(0);\n\n    han.resetCanvas();\n    }\n\n    cout << \"Finished...!\\n\";\n}\n\n\nint demo_easy() {\n    cout <<\"demo_easy\\n\";\n\n    Plot2Mat han;\n    han.setYminmax( -2, 2 );\n\n    VectorXd y = VectorXd::Random(10);\n    cout << \"y=\" << y.transpose() << endl;\n\n    han.mark( 4 );\n    han.plot(y, cv::Scalar(0,255,0), true, true);\n    // han.plot(y);\n    cv::imshow(\"canvas\", han.getCanvasConstPtr() );\n    cv::waitKey(0);\n\n\n}\n\nint main() {\n    demo_easy();\n    // demo();\n}\n"
  },
  {
    "path": "src/unittest/unittest_pose_tester.cpp",
    "content": "// This unittest will test and evaluate\n// ` StaticTheiaPoseCompute::P3P_ICP` and `StaticTheiaPoseCompute::PNP`\n// from DlsPnpWithRansac.h/cpp\n//  Author  : Manohar Kuse <mpkuse@connect.ust.hk>\n//  Created : 14th May, 2019\n//\n\n\n#include <iostream>\n#include <string>\n\n#include \"camodocal/camera_models/Camera.h\"\n#include \"camodocal/camera_models/CameraFactory.h\"\n#include \"camodocal/camera_models/CataCamera.h\"\n#include \"camodocal/camera_models/EquidistantCamera.h\"\n\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n// Eigen\n#include <Eigen/Dense>\n#include <Eigen/Geometry>\nusing namespace Eigen;\nusing namespace std;\n\n// custom\n#include \"../utils/CameraGeometry.h\"\n#include \"../utils/TermColor.h\"\n#include \"../utils/RawFileIO.h\"\n\n#include \"../utils/PointFeatureMatching.h\"\n// #include \"../utils/GMSMatcher/gms_matcher.h\"\n\n\n#include \"../DlsPnpWithRansac.h\"\n\nconst std::string BASE = \"/Bulk_Data/_tmp/\";\n\nstd::shared_ptr<StereoGeometry> make_stereo_geom()\n{\n    //--------- Intrinsics load\n    camodocal::CameraPtr left_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(BASE+\"/cameraIntrinsic.0.yaml\");\n    camodocal::CameraPtr right_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(BASE+\"/cameraIntrinsic.1.yaml\");\n    // camodocal::CameraPtr left_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(string(BASE+\"../leveled_cam_pinhole/\")+\"/camera_left.yaml\");\n    // camodocal::CameraPtr right_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(string(BASE+\"../leveled_cam_pinhole/\")+\"/camera_right.yaml\");\n\n    cout << left_camera->parametersToString() << endl;\n    cout << right_camera->parametersToString() << endl;\n\n    if( !left_camera || !right_camera )\n    {\n        cout << \"\\nERROR : Abstract stereo Camera is not set. You need to init camera before setting it to geometry clases\\n\";\n        exit(10);\n    }\n\n\n    //----------- Stereo Base line load (alsoed called extrinsic calibration)\n        // mynt eye\n    // Vector4d q_xyzw = Vector4d( -7.0955103716253032e-04,-1.5775578725333590e-03,-1.2732644461763854e-03,9.9999769332040711e-01 );\n    // Vector3d tr_xyz = Vector3d( -1.2006984141573309e+02/1000.,3.3956264524978619e-01/1000.,-1.6784055634087214e-01/1000. );\n\n        // realsense\n    Vector4d q_xyzw = Vector4d( 0.0, 0.0, 0.0, 1.0 );\n    Vector3d tr_xyz = Vector3d( -49.8/1000., 0.0/1000., 0.0/1000. );\n\n    Matrix4d right_T_left;\n    PoseManipUtils::raw_xyzw_to_eigenmat( q_xyzw, tr_xyz, right_T_left );\n    cout << \"XXright_T_left: \" << PoseManipUtils::prettyprintMatrix4d( right_T_left ) << endl;\n\n    // StereoGeometry* sthm = new StereoGeometry( left_camera, right_camera, right_T_left );\n    // cout << \"sthn->get_K() \" << sthm->get_K() << endl;\n\n\n    // StereoGeometry NNN = StereoGeometry( left_camera, right_camera, right_T_left );\n    // cout << \"NNN->get_K() \" << NNN.get_K() << endl;\n\n    //-------------------- init stereogeom\n    std::shared_ptr<StereoGeometry> stereogeom;\n    stereogeom = std::make_shared<StereoGeometry>( left_camera,right_camera,     right_T_left  );\n    // stereogeom->set_K( 375.0, 375.0, 376.0, 240.0 );\n\n    cout << \"[in make_stereo_geom]\" << stereogeom->get_K() << endl;\n\n    return stereogeom;\n\n}\n\nbool compute_rel_pose( std::shared_ptr<StereoGeometry> stereogeom, int frame_a, int frame_b )\n{\n    //------------------------------------------------\n    //------ 3d points from frame_a\n    //------------------------------------------------\n    cout << TermColor::iGREEN() << \"LOAD[A]: BASE/\"+std::to_string(frame_a)+\".jpg\" << TermColor::RESET() << endl;\n    cv::Mat a_imleft_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_a)+\".jpg\", 0 );\n    cv::Mat a_imright_raw = cv::imread( BASE+\"/\"+std::to_string(frame_a)+\"_1.jpg\", 0 );\n    Matrix4d wTA;\n    RawFileIO::read_eigen_matrix( BASE+\"/\"+std::to_string(frame_a)+\".wTc\", wTA );\n    cout << \"wTA: \" << PoseManipUtils::prettyprintMatrix4d( wTA ) << endl;\n    if( a_imright_raw.empty() || a_imright_raw.empty() )\n    {\n        cout << TermColor::RED() << \"Cannot read image with idx=\" << frame_a << \" perhaps filenotfound\" << TermColor::RESET() << endl;\n        return false;\n    }\n\n\n    cv::Mat a_imleft_srectified, a_imright_srectified;\n    cv::Mat a_3dImage;\n    MatrixXd a_3dpts;\n    cv::Mat a_disp_viz;\n    stereogeom->get_srectifiedim_and_3dpoints_and_3dmap_and_disparity_from_raw_images( a_imleft_raw, a_imright_raw,\n        a_imleft_srectified,a_imright_srectified,  a_3dpts, a_3dImage, a_disp_viz );\n\n\n\n    //-----------------------------------------------------\n    //--------------- 3d points from frame_b\n    //-----------------------------------------------------\n    cout << TermColor::iGREEN() << \"LOAD[B]: BASE/\"+std::to_string(frame_b)+\".jpg\" << TermColor::RESET() << endl;\n    cv::Mat b_imleft_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_b)+\".jpg\", 0 );\n    cv::Mat b_imright_raw = cv::imread( BASE+\"/\"+std::to_string(frame_b)+\"_1.jpg\", 0 );\n    Matrix4d wTB;\n    RawFileIO::read_eigen_matrix( BASE+\"/\"+std::to_string(frame_b)+\".wTc\", wTB );\n    cout << \"wTB: \" << PoseManipUtils::prettyprintMatrix4d( wTB ) << endl;\n    if( b_imleft_raw.empty() || b_imright_raw.empty() )\n    {\n        cout << TermColor::RED() << \"Cannot read image with idx=\" << frame_b << \" perhaps filenotfound\" << TermColor::RESET() << endl;\n        return false;\n    }\n    cv::Mat b_imleft_srectified, b_imright_srectified;\n    cv::Mat b_3dImage;\n    MatrixXd b_3dpts;\n    cv::Mat b_disp_viz;\n    stereogeom->get_srectifiedim_and_3dpoints_and_3dmap_and_disparity_from_raw_images( b_imleft_raw, b_imright_raw,\n                            b_imleft_srectified, b_imright_srectified,  b_3dpts, b_3dImage, b_disp_viz );\n\n\n    cout << \"ODOM b_T_a: \" << PoseManipUtils::prettyprintMatrix4d( wTB.inverse() * wTA ) << endl;\n\n    //---------------------------------------------------------------------\n    //------------ point matches between a_left, b_left\n    //---------------------------------------------------------------------\n    cout << TermColor::iGREEN() << \"point matches between a_left, b_left\" << TermColor::RESET() << endl;\n    MatrixXd uv, uv_d; // u is from frame_a; ud is from frame_b\n    ElapsedTime timer;\n    timer.tic();\n    StaticPointFeatureMatching::gms_point_feature_matches(a_imleft_srectified, b_imleft_srectified, uv, uv_d );\n    string msg_pf_matches = to_string( timer.toc_milli() )+\" (ms) elapsed time for point_feature_matches computation\";\n    if( uv.cols() < 30 ) {\n        cout << TermColor::RED() << \"too few gms matches between \" << frame_a << \" and \" << frame_b << TermColor::RESET() << endl;\n        return false;\n    }\n\n\n    #if 1 // plot ppoint-feature matches\n    cv::Mat dst_feat_matches;\n    MiscUtils::plot_point_pair( a_imleft_srectified, uv, frame_a,\n                     b_imleft_srectified, uv_d, frame_b,\n                        dst_feat_matches, 3, msg_pf_matches+\";#pf-matches: \"+to_string( uv.cols() )  );\n    cv::resize(dst_feat_matches, dst_feat_matches, cv::Size(), 0.5, 0.5 );\n    cv::imshow( \"dst_feat_matches\", dst_feat_matches );\n    #endif\n\n    #if 0  // disparty maps of both images\n    cv::Mat dst_disp;\n    MiscUtils::side_by_side( a_disp_viz, b_disp_viz, dst_disp );\n    MiscUtils::append_status_image( dst_disp, \"a=\"+ to_string(frame_a)+\"     b=\"+to_string(frame_b), .8 );\n    cv::resize(dst_disp, dst_disp, cv::Size(), 0.5, 0.5 );\n    cv::imshow( \"dst_disp\", dst_disp );\n    #endif\n\n\n    //----------------------------------------------------------------------\n    //----------- compute pose\n    //----------------------------------------------------------------------\n    cout << TermColor::iGREEN() << \"compute pose\" << TermColor::RESET() << endl;\n    #if 0 // pnp( 3d_pts_of_a, 2d_pts_of_b )\n    cout << TermColor::iBLUE() << \" pnp( 3d_pts_of_a, 2d_pts_of_b )\" << TermColor::RESET() << endl;\n\n\n    //-----------prep data\n    std::vector<Eigen::Vector2d> feature_position_uv;   //< these will be normalized image co-ordinates\n    std::vector<Eigen::Vector2d> feature_position_uv_d; //< these will be normalized image co-ordinates\n    std::vector<Eigen::Vector3d> world_point_uv;\n    StaticPointFeatureMatching::make_3d_2d_collection__using__pfmatches_and_disparity(\n        stereogeom, uv, a_3dImage, uv_d,\n                                feature_position_uv, feature_position_uv_d, world_point_uv);\n\n\n    //----------initial guess\n    Matrix4d out_b_T_a = Matrix4d::Identity();\n    out_b_T_a = wTB.inverse() * wTA ;\n    cout << TermColor::YELLOW() <<  \"initial_guess___out_b_T_a : \" << PoseManipUtils::prettyprintMatrix4d( out_b_T_a ) << TermColor::RESET() << endl;\n\n\n    //---------- PNP\n    string ceres_pnp_msh;\n    StaticCeresPoseCompute::PNP( world_point_uv, feature_position_uv_d, out_b_T_a, ceres_pnp_msh );\n\n    Matrix4d out_b_T_a111 = Matrix4d::Identity();\n    string usual_msg;\n    StaticTheiaPoseCompute::PNP( world_point_uv, feature_position_uv_d, out_b_T_a111, usual_msg );\n    cout << TermColor::YELLOW() <<  \"final_soltion___out_b_T_a(theia) : \" << PoseManipUtils::prettyprintMatrix4d( out_b_T_a111 ) << TermColor::RESET() << endl;\n\n\n    //--------final solution\n    cout << TermColor::YELLOW() <<  \"final_soltion___out_b_T_a(ceres) : \" << PoseManipUtils::prettyprintMatrix4d( out_b_T_a ) << TermColor::RESET() << endl;\n\n\n    #if 1 //verification\n\n    //---------------------------------------------\n    //--- Use the theia's estimated pose do PI( b_T_a * 3dpts ) and plot these\n    //--- points on B. Also plot detected points of B\n    //---------------------------------------------\n    #if 1\n        MatrixXd _3dpts_of_A_projectedonB = MatrixXd::Zero(3, world_point_uv.size() );\n        MatrixXd _detectedpts_of_B = MatrixXd::Zero(3, world_point_uv.size() );\n        MatrixXd _detectedpts_of_A = MatrixXd::Zero(3, world_point_uv.size() );\n\n        int n_good = 0;\n        for( int i=0 ; i<world_point_uv.size() ; i++ ) {\n            Vector4d a_X_i;\n            a_X_i << Vector3d(world_point_uv[i]), 1.0;\n            Vector4d b_X_i = out_b_T_a * a_X_i;\n            Vector3d _X_i = b_X_i.topRows(3);\n            _X_i /= _X_i(2); // Z-division for projection\n            _3dpts_of_A_projectedonB.col(i) = stereogeom->get_K() * _X_i; //< scaling with camera-intrinsic matrix\n\n\n            Vector3d _tmp;\n            _tmp << feature_position_uv_d[i], 1.0 ;\n            _detectedpts_of_B.col(i) = stereogeom->get_K() * _tmp;\n\n            _tmp << feature_position_uv[i], 1.0 ;\n            _detectedpts_of_A.col(i) = stereogeom->get_K() * _tmp;\n\n\n            Vector3d delta = _3dpts_of_A_projectedonB.col(i) - _detectedpts_of_B.col(i);\n            // cout << \"i=\" << i << \" \";\n            if( abs(delta(0)) < 2. && abs(delta(1)) < 2. ) {\n                // cout << \"  delta=\" << delta.transpose();\n                n_good++;\n            }\n            else {\n                // cout << TermColor::RED() << \"delta=\" << delta.transpose() << TermColor::RESET();\n            }\n            // cout << endl;\n        }\n        cout << \"n_good=\" <<n_good << \" of \" << world_point_uv.size() << endl;\n    #endif // PI( b_T_a * 3dpts )\n\n\n\n\n    //------------------------------\n    //--- Use relative pose of obometry to do PI( odom_b_T_a * 3dpts ) and plot these on image-b\n    //--- load odometry poses\n    //-------------------------------\n    #if 1\n        MatrixXd _3dpts_of_A_projectedonB_with_odom_rel_pose = MatrixXd::Zero(3, world_point_uv.size() );\n        // Matrix4d wTA;\n        // RawFileIO::read_eigen_matrix( BASE+\"/\"+std::to_string(frame_a)+\".wTc\", wTA );\n        // cout << \"wTa(odom)\" << PoseManipUtils::prettyprintMatrix4d( wTa ) << endl;\n        // Matrix4d wTB;\n        // RawFileIO::read_eigen_matrix( BASE+\"/\"+std::to_string(frame_b)+\".wTc\", wTB );\n        // cout << \"wTb(odom)\" << PoseManipUtils::prettyprintMatrix4d( wTb ) << endl;\n        Matrix4d odom_b_T_a = wTB.inverse() * wTA;\n        cout << \"odom_b_T_a\" << PoseManipUtils::prettyprintMatrix4d( odom_b_T_a ) << endl;\n\n        // PI( odom_b_T_a * a_X )\n        for( int i=0 ; i<world_point_uv.size() ; i++ ) {\n            Vector4d a_X_i;\n            a_X_i << Vector3d(world_point_uv[i]), 1.0;\n            Vector4d b_X_i = odom_b_T_a * a_X_i;\n            Vector3d _X_i = b_X_i.topRows(3);\n            _X_i /= _X_i(2); // Z-division for projection\n            _3dpts_of_A_projectedonB_with_odom_rel_pose.col(i) = stereogeom->get_K() * _X_i; //< scaling with camera-intrinsic matrix\n        }\n\n    #endif // PI( odom_b_T_a * 3dpts )\n\n\n    #if 1\n        // plot( B, ud )\n        cv::Mat __dst;\n        MiscUtils::plot_point_sets( b_imleft_srectified, _detectedpts_of_B, __dst, cv::Scalar( 0,0,255), false, \"_detectedpts_of_B (red)\" );\n        // cv::imshow( \"plot( B, ud )\", __dst );\n\n        // plot( B, _3dpts_of_A_projectedonB )\n        MiscUtils::plot_point_sets( __dst, _3dpts_of_A_projectedonB, cv::Scalar( 0,255,255), false, \";_3dpts_of_A_projectedonB, PI( b_T_a * X),(yellow)\" );\n        // MiscUtils::plot_point_sets( b_imleft_srectified, _3dpts_of_A_projectedonB, __dst, cv::Scalar( 0,255,255), false, \"_3dpts_of_A_projectedonB\" );\n\n        MiscUtils::plot_point_sets( __dst, _detectedpts_of_A, cv::Scalar( 255,255,255), false, \";;_detectedpts_of_A(white)\" );\n\n        MiscUtils::plot_point_sets( __dst, _3dpts_of_A_projectedonB_with_odom_rel_pose, cv::Scalar( 255,0,0), false, \";;;_3dpts_of_A_projectedonB_with_odom_rel_pose, PI( odom_b_T_a * X),(blue)\" );\n\n        string status_msg = \"\";\n        status_msg += \"_detectedpts_of_B (red);\";\n        status_msg += \"_3dpts_of_A_projectedonB, PI( b_T_a * X),(yellow);\";\n        status_msg += \"_detectedpts_of_A(white);\";\n        status_msg += \"_3dpts_of_A_projectedonB_with_odom_rel_pose, PI( odom_b_T_a * X),(blue)\";\n        MiscUtils::append_status_image( __dst,  status_msg );\n\n        cv::imshow( \"b_imleft_srectified\", __dst );\n    #endif //plotting\n\n    #endif //verification\n\n    #endif //option-1\n\n\n\n    #if 1 // p3p( 3d_pts_of_a, 3d_pts_of_b )\n    cout << TermColor::iBLUE() << \" p3p( 3d_pts_of_a, 3d_pts_of_b )\" << TermColor::RESET() << endl;\n\n    //---------prep data\n    vector< Vector3d> uv_X;\n    vector< Vector3d> uvd_Y;\n    StaticPointFeatureMatching::make_3d_3d_collection__using__pfmatches_and_disparity( uv, a_3dImage, uv_d, b_3dImage,\n        uv_X, uvd_Y );\n\n    //-------------initial guess\n    Matrix4d icp_b_T_a;\n    icp_b_T_a = wTB.inverse() * wTA ;\n    cout << TermColor::YELLOW() <<  \"initial icp_b_T_a(ceres) : \" << PoseManipUtils::prettyprintMatrix4d( icp_b_T_a ) << TermColor::RESET() << endl;\n\n\n    //------------------p3p (icp)\n    string p3p__msg;\n    // float p3p_goodness = StaticTheiaPoseCompute::P3P_ICP( uv_X, uvd_Y, icp_b_T_a, p3p__msg );\n    float p3p_goodness = StaticCeresPoseCompute::P3P_ICP( uv_X, uvd_Y, icp_b_T_a, p3p__msg );\n\n\n\n    //--------final solution\n    cout << TermColor::YELLOW() <<  \"final icp_b_T_a(ceres) : \" << PoseManipUtils::prettyprintMatrix4d( icp_b_T_a ) << TermColor::RESET() << endl;\n\n    #endif //option-3\n\n}\n\nint main(int argc, char ** argv )\n{\n    //---\n    cout << TermColor::iGREEN() << \"StereoGeometry\\n\" << TermColor::RESET() ;\n    auto stereogeom = make_stereo_geom();\n\n\n    //--- Cmdline\n    if( argc != 3 ) {\n        cout << \"[MAIN::INVALID USAGE] I am expecting you to give two numbers in the command line between which you want to compute the pose\\n\";\n        exit(1);\n    }\n    Matrix4d out_b_T_a = Matrix4d::Identity();\n    int a = stoi( argv[1] );\n    int b = stoi( argv[2] );\n\n\n    //--\n    compute_rel_pose( stereogeom, a, b );\n    cv::waitKey(0);\n\n\n\n}\n\n\n#include \"../utils/nlohmann/json.hpp\"\nusing json = nlohmann::json;\nint main1( int argc, char ** argv )\n{\n    std::shared_ptr<StereoGeometry> stereogeom = make_stereo_geom();\n\n    // load ProcessedLoopCandidate.json\n    cout << \"Open JSON : \" << BASE+\"/matching/ProcessedLoopCandidate.json\" << endl;\n    std::ifstream fp(BASE+\"/matching/ProcessedLoopCandidate.json\");\n    json proc_candi_json;\n    fp >> proc_candi_json;\n    for( int i =0 ; i<proc_candi_json.size() ; i++ ) // loop over all the candidates\n    {\n        int a = proc_candi_json[i][\"idx_a\"];\n        int b = proc_candi_json[i][\"idx_b\"];\n        cout << a << \" \" << b << endl;\n        bool status = compute_rel_pose( stereogeom, a, b );\n        cv::waitKey(0);\n\n    }\n}\n"
  },
  {
    "path": "src/unittest/unittest_rosservice_client.cpp",
    "content": "#include <ros/ros.h>\n#include <cerebro/WholeImageDescriptorCompute.h>\n#include <iostream>\n\n#include <sensor_msgs/Image.h>\n\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <cv_bridge/cv_bridge.h>\n\n\nint main( int argc, char ** argv )\n{\n    // Ros INIT and Make connection to Server\n    ros::init( argc, argv, \"unittest_rosservice_client_cpp\" );\n    ros::NodeHandle n;\n    ros::ServiceClient client = n.serviceClient<cerebro::WholeImageDescriptorCompute>( \"whole_image_descriptor_compute\" );\n    client.waitForExistence();\n    if( !client.exists() ) {\n        ROS_ERROR( \"Connection to server NOT successful\" );\n        return 0;\n    }\n    else std::cout << \"Connection to server established\\n\";\n\n    // Make service message\n    cerebro::WholeImageDescriptorCompute srv; //service message\n    cv::Mat im = cv::imread( \"/app/lena.jpg\") ;\n    cv::Mat im_resized;\n    cv::resize( im, im_resized, cv::Size(640, 512) );\n    cv::imshow( \"lena\", im );\n    cv::waitKey(0);\n\n    // call\n    sensor_msgs::ImagePtr image_msg = cv_bridge::CvImage(std_msgs::Header(), \"bgr8\", im_resized).toImageMsg();\n    srv.request.ima = *image_msg;\n    srv.request.a = 678;\n    if( client.call( srv ) ) {\n        ROS_INFO( \"Received response from server\" );\n        std::cout << \"desc.size=\" << srv.response.desc.size() << std::endl;\n    }\n    else {\n        ROS_ERROR( \"Failed to call ros service\" );\n    }\n\n    return 0;\n\n}\n"
  },
  {
    "path": "src/unittest/unittest_termcolor.cpp",
    "content": "#include <iostream>\n\n#include \"../utils/TermColor.h\"\n// #include \"../utils/PoseManipUtils.h\"\n\nusing namespace std;\nint main()\n{\n    cout << \"Hello World\\n\";\n    cout << TermColor::RED() << \"Hello World in red\" << TermColor::RESET() << endl;\n    cout << TermColor::iRED() << \"Hello World in red\" << TermColor::RESET() << endl;\n\n\n    cout << TermColor::compose( TermColor::CTRL_BOLD, TermColor::FG_RED, true ) << \"Hello\" << TermColor::RESET() << endl;\n    cout << TermColor::compose( TermColor::CTRL_BOLD, TermColor::FG_RED, false ) << \"Hello\" << TermColor::RESET() << endl;\n    cout << TermColor::compose( TermColor::FG_BLUE, true ) << \"Hello\" << TermColor::RESET() << endl;\n    cout << TermColor::compose( TermColor::FG_BLUE, false ) << \"Hello\" << TermColor::RESET() << endl;\n\n    cout << TermColor::compose( TermColor::BG_CYAN ) << \"Hello\" << TermColor::RESET() << endl;\n    cout << TermColor::compose( TermColor::CTRL_UNDERLINE, TermColor::BG_GREEN ) << \"Hello\" << TermColor::RESET() << endl;\n    return 0;\n}\n"
  },
  {
    "path": "src/unittest/unittest_theia.cpp",
    "content": "// Opens the log.json file :\n// A) Plots the vio poses\n// B) plot loop candidates\n// c) at loop candidate compute relative pose using pnp\n// d) plot new camera and old camera.\n\n\n#include <iostream>\n#include <string>\n\n#include \"camodocal/camera_models/Camera.h\"\n#include \"camodocal/camera_models/CameraFactory.h\"\n#include \"camodocal/camera_models/CataCamera.h\"\n#include \"camodocal/camera_models/EquidistantCamera.h\"\n\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n#include \"../utils/CameraGeometry.h\"\n\n\n#include \"../utils/MiscUtils.h\"\n#include \"../utils/ElapsedTime.h\"\n#include \"../utils/PoseManipUtils.h\"\n#include \"../utils/TermColor.h\"\n#include \"../utils/RawFileIO.h\"\n#include \"../utils/RosMarkerUtils.h\"\n\n#include \"../utils/nlohmann/json.hpp\"\nusing json = nlohmann::json;\n\n#include \"../utils/GMSMatcher/gms_matcher.h\"\n#include <theia/theia.h>\n\n\nconst std::string BASE = \"/Bulk_Data/_tmp/\";\n\n\n\n\n/////////// DlsPnp-RANSAC ///////////////////\n// Data\nstruct CorrespondencePair_3d2d {\n    Vector3d a_X; // 3d point expressed in co-ordinate system of `image-a`\n    Vector2d uv_d; //feature point in normalized-image co-ordinates. of the other view (b)\n};\n\n// Model\nstruct RelativePose {\n    Matrix4d b_T_a;\n};\n\n\nclass DlsPnpWithRansac: public theia::Estimator<CorrespondencePair_3d2d, RelativePose> {\npublic:\n    // Number of points needed to estimate a line.\n    double SampleSize() const  { return 15; }\n\n    // Estimate a pose from N points\n    bool EstimateModel( const std::vector<CorrespondencePair_3d2d>& data,\n                            std::vector<RelativePose>* models) const {\n\n        // cout << \"EstimateModel: data.size=\"<< data.size() << \" \" <<  data[0].uv_d.transpose() << endl;\n        std::vector<Vector2d> feature_position;\n        std::vector<Vector3d> world_point;\n        for( int i=0 ; i<data.size() ; i++ ) {\n            feature_position.push_back( data[i].uv_d );\n            world_point.push_back( data[i].a_X );\n        }\n\n        std::vector<Quaterniond> solution_rotations;\n        std::vector<Vector3d> solution_translations;\n\n        theia::DlsPnp( feature_position, world_point, &solution_rotations, &solution_translations );\n        if( solution_rotations.size() == 1 ) {\n            RelativePose r;\n            r.b_T_a = Matrix4d::Identity();\n            r.b_T_a.topLeftCorner(3,3) = solution_rotations[0].toRotationMatrix();\n            r.b_T_a.col(3).topRows(3) = solution_translations[0];\n            models->push_back( r );\n            return true;\n        }\n        else {\n            return false;\n        }\n    }\n\n\n    double Error( const CorrespondencePair_3d2d& point, const RelativePose& r ) const {\n        Vector3d  b_X = r.b_T_a.topLeftCorner(3,3) * point.a_X + r.b_T_a.col(3).topRows(3);\n        double depth = b_X(2);\n        b_X /= depth;\n        double reproj_error = abs(b_X(0) - point.uv_d(0)) + abs(b_X(1) - point.uv_d(1));\n\n        // I want nearby points to have smaller Error than far off points.\n        // So artificially increase the error when depth is higher for the point.\n        double f = 1.;\n        #if 0\n        if( depth < 1. )\n            f = .5;\n        if( depth >=1 && depth < 3 )\n            f = 5.0;\n        if( depth >=3 && depth < 8 )\n            f = 2.;\n        if( depth >= 8. && depth < 15 )\n            f = 1.7;\n        if( depth >= 15 )\n            f = 0.1;\n        #endif\n\n        return reproj_error*f;\n\n    }\n};\n/////////// END DlsPnp-RANSAC ///////////////////\n\n\n\n//////////////////// AlignPointCloudsUmeyama with Ransac ///////////////////////\n// Data\nstruct CorrespondencePair_3d3d {\n    Vector3d a_X; // 3d point expressed in co-ordinate system of `image-a`\n    Vector3d b_X; // 3d point expressed in co-ordinate system of `image-b`\n};\n\n// Model\n// struct RelativePose {\n    // Matrix4d b_T_a;\n// };\n\nclass AlignPointCloudsUmeyamaWithRansac: public theia::Estimator<CorrespondencePair_3d3d, RelativePose> {\npublic:\n    // Number of points needed to estimate a line.\n    double SampleSize() const  { return 10; }\n\n    bool EstimateModel( const std::vector<CorrespondencePair_3d3d>& data,\n                            std::vector<RelativePose>* models) const {\n        //TODO\n\n        std::vector<Vector3d> a_X;\n        std::vector<Vector3d> b_X;\n        for( int i=0 ; i<data.size() ; i++ ) {\n            a_X.push_back( data[i].a_X );\n            b_X.push_back( data[i].b_X );\n        }\n\n        Matrix3d ____R;\n        Vector3d ____t; double ___s=1.0;\n        theia::AlignPointCloudsUmeyama( a_X, b_X, &____R, &____t, &___s );\n\n        if( min( ___s, 1.0/___s ) > 0.9 ) {\n            RelativePose r;\n            r.b_T_a = Matrix4d::Identity();\n            r.b_T_a.topLeftCorner(3,3) = ____R;\n            r.b_T_a.col(3).topRows(3) = ____t;\n            models->push_back( r );\n            // cout << \"Estimate s=\" << ___s << \" \" << PoseManipUtils::prettyprintMatrix4d(r.b_T_a)<< endl;\n            return true;\n        } else {\n            return false;\n        }\n\n    }\n\n    double Error( const CorrespondencePair_3d3d& point, const RelativePose& r ) const {\n        Vector3d  b_X_cap = r.b_T_a.topLeftCorner(3,3) * point.a_X + r.b_T_a.col(3).topRows(3);\n\n        double err = (b_X_cap - point.b_X).norm();\n\n        double f=1.0;\n        if( point.a_X(2) < 1. && point.a_X(2) > 8. )\n            f = 1.2;\n\n        // cout << \"ICP RAnsac error=\" << err << endl;\n\n        return f*err;\n\n    }\n\n};\n\n//////////////////// END AlignPointCloudsUmeyama with Ransac ///////////////////////\n\nvoid point_feature_matches( const cv::Mat& imleft_undistorted, const cv::Mat& imright_undistorted,\n                            MatrixXd& u, MatrixXd& ud )\n{\n    ElapsedTime timer;\n\n\n    //\n    // Point feature and descriptors extract\n    std::vector<cv::KeyPoint> kp1, kp2;\n    cv::Mat d1, d2; //< descriptors\n\n    cv::Ptr<cv::ORB> orb = cv::ORB::create(5000);\n    orb->setFastThreshold(0);\n\n    timer.tic();\n    orb->detectAndCompute(imleft_undistorted, cv::Mat(), kp1, d1);\n    orb->detectAndCompute(imright_undistorted, cv::Mat(), kp2, d2);\n    cout <<  timer.toc_milli()  << \" (ms) 2X detectAndCompute(ms) : \"<< endl;\n    // std::cout << \"d1 \" << MiscUtils::cvmat_info( d1 ) << std::endl;\n    // std::cout << \"d2 \" << MiscUtils::cvmat_info( d2 ) << std::endl;\n\n    //plot\n    // cv::Mat dst_left, dst_right;\n    // MatrixXd e_kp1, e_kp2;\n    // MiscUtils::keypoint_2_eigen( kp1, e_kp1 );\n    // MiscUtils::keypoint_2_eigen( kp2, e_kp2 );\n    // MiscUtils::plot_point_sets( imleft_undistorted, e_kp1, dst_left, cv::Scalar(0,0,255), false );\n    // MiscUtils::plot_point_sets( imright_undistorted, e_kp2, dst_right, cv::Scalar(0,0,255), false );\n    // cv::imshow( \"dst_left\", dst_left );\n    // cv::imshow( \"dst_right\", dst_right );\n\n    //\n    // Point feature matching\n    cv::BFMatcher matcher(cv::NORM_HAMMING); // TODO try FLANN matcher here.\n    vector<cv::DMatch> matches_all, matches_gms;\n    timer.tic();\n    matcher.match(d1, d2, matches_all);\n    std::cout << timer.toc_milli() << \" : (ms) BFMatcher took (ms)\\t\";\n    std::cout << \"BFMatcher : npts = \" << matches_all.size() << std::endl;\n\n\n    // gms_matcher\n    timer.tic();\n    std::vector<bool> vbInliers;\n    gms_matcher gms(kp1, imleft_undistorted.size(), kp2, imright_undistorted.size(), matches_all);\n    int num_inliers = gms.GetInlierMask(vbInliers, false, false);\n    cout << timer.toc_milli() << \" : (ms) GMSMatcher took.\\t\" ;\n    cout << \"Got total gms matches \" << num_inliers << \" matches.\" << endl;\n\n    // collect matches\n    for (size_t i = 0; i < vbInliers.size(); ++i)\n    {\n        if (vbInliers[i] == true)\n        {\n            matches_gms.push_back(matches_all[i]);\n        }\n    }\n    // MatrixXd M1, M2;\n    MiscUtils::dmatch_2_eigen( kp1, kp2, matches_gms, u, ud, true );\n\n\n}\n\n\n\n\nstd::shared_ptr<StereoGeometry> make_stereo_geom()\n{\n    //--------- Intrinsics load\n    camodocal::CameraPtr left_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(BASE+\"/cameraIntrinsic.0.yaml\");\n    camodocal::CameraPtr right_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(BASE+\"/cameraIntrinsic.1.yaml\");\n    // camodocal::CameraPtr left_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(string(BASE+\"../leveled_cam_pinhole/\")+\"/camera_left.yaml\");\n    // camodocal::CameraPtr right_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(string(BASE+\"../leveled_cam_pinhole/\")+\"/camera_right.yaml\");\n\n    cout << left_camera->parametersToString() << endl;\n    cout << right_camera->parametersToString() << endl;\n\n    if( !left_camera || !right_camera )\n    {\n        cout << \"\\nERROR : Abstract stereo Camera is not set. You need to init camera before setting it to geometry clases\\n\";\n        exit(10);\n    }\n\n\n    //----------- Stereo Base line load (alsoed called extrinsic calibration)\n        // mynt eye\n    Vector4d q_xyzw = Vector4d( -7.0955103716253032e-04,-1.5775578725333590e-03,-1.2732644461763854e-03,9.9999769332040711e-01 );\n    Vector3d tr_xyz = Vector3d( -1.2006984141573309e+02/1000.,3.3956264524978619e-01/1000.,-1.6784055634087214e-01/1000. );\n    Matrix4d right_T_left;\n    PoseManipUtils::raw_xyzw_to_eigenmat( q_xyzw, tr_xyz, right_T_left );\n    cout << \"XXright_T_left: \" << PoseManipUtils::prettyprintMatrix4d( right_T_left ) << endl;\n\n    // StereoGeometry* sthm = new StereoGeometry( left_camera, right_camera, right_T_left );\n    // cout << \"sthn->get_K() \" << sthm->get_K() << endl;\n\n\n    // StereoGeometry NNN = StereoGeometry( left_camera, right_camera, right_T_left );\n    // cout << \"NNN->get_K() \" << NNN.get_K() << endl;\n\n    //-------------------- init stereogeom\n    std::shared_ptr<StereoGeometry> stereogeom;\n    stereogeom = std::make_shared<StereoGeometry>( left_camera,right_camera,     right_T_left  );\n    // stereogeom->set_K( 375.0, 375.0, 376.0, 240.0 );\n\n    cout << \"[in make_stereo_geom]\" << stereogeom->get_K() << endl;\n\n    return stereogeom;\n\n}\n\n\n// will return false if cannot compute pose\nbool relative_pose_compute_with_theia( std::shared_ptr<StereoGeometry> stereogeom,\n                        int frame_a, int frame_b,\n                        Matrix4d& out_b_T_a )\n{\n    ElapsedTime timer;\n\n    //------------------------------------------------\n    //------ 3d points from frame_a\n    //------------------------------------------------\n    cout << TermColor::iGREEN() << \"LOAD: BASE/\"+std::to_string(frame_a)+\".jpg\" << TermColor::RESET() << endl;\n    cv::Mat a_imleft_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_a)+\".jpg\", 0 );\n    cv::Mat a_imright_raw = cv::imread( BASE+\"/\"+std::to_string(frame_a)+\"_1.jpg\", 0 );\n    if( a_imright_raw.empty() || a_imright_raw.empty() )\n    {\n        cout << TermColor::RED() << \"Cannot read image with idx=\" << frame_a << \" perhaps filenotfound\" << TermColor::RESET() << endl;\n        return false;\n    }\n    cv::Mat a_imleft_srectified, a_imright_srectified;\n    cv::Mat _3dImage;\n    MatrixXd _3dpts;\n    // stereogeom->get3dpoints_and_3dmap_from_raw_images( a_imleft_raw, a_imright_raw,\n                        // _3dpts, _3dImage,\n                        // a_imleft_srectified, a_imright_srectified );\n\n    cv::Mat disp_viz;\n    timer.tic();\n    stereogeom->get_srectifiedim_and_3dpoints_and_3dmap_and_disparity_from_raw_images( a_imleft_raw, a_imright_raw,\n        a_imleft_srectified,a_imright_srectified,  _3dpts, _3dImage, disp_viz );\n    cout << timer.toc_milli() << \" : (ms) get_srectifiedim_and_3dpoints_and_3dmap_and_disparity_from_raw_images\\n\";\n\n\n    #if 1\n    cv::imshow( \"a_imleft_raw\", a_imleft_raw );\n    cv::imshow( \"a_imright_raw\", a_imright_raw );\n    // cv::imshow( \"a_imleft_srectified\", a_imleft_srectified );\n    // cv::imshow( \"a_imright_srectified\", a_imright_srectified );\n    cv::imshow( \"a_disp_viz\", disp_viz );\n    #endif\n\n    //-----------------------------------------------------\n    //--------------- stereo rectify b\n    //-----------------------------------------------------\n    cout << TermColor::iGREEN() << \"LOAD: BASE/\"+std::to_string(frame_b)+\".jpg\" << TermColor::RESET() << endl;\n    cv::Mat b_imleft_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_b)+\".jpg\", 0 );\n    cv::Mat b_imright_raw = cv::imread( BASE+\"/\"+std::to_string(frame_b)+\"_1.jpg\", 0 );\n    if( b_imleft_raw.empty() || b_imright_raw.empty() )\n    {\n        cout << TermColor::RED() << \"Cannot read image with idx=\" << frame_b << \" perhaps filenotfound\" << TermColor::RESET() << endl;\n        return false;\n    }\n    cv::Mat b_imleft_srectified, b_imright_srectified;\n    stereogeom->do_stereo_rectification_of_raw_images( b_imleft_raw, b_imright_raw,\n                            b_imleft_srectified, b_imright_srectified );\n\n\n\n    //---------------------------------------------------------------------\n    //------------ point matches between a_left, b_left\n    //---------------------------------------------------------------------\n    MatrixXd u, ud; // u is from frame_a; ud is from frame_b\n    point_feature_matches(a_imleft_srectified, b_imleft_srectified, u, ud );\n    if( u.cols() < 30 ) {\n        cout << TermColor::RED() << \"too few gms matches between \" << frame_a << \" and \" << frame_b << TermColor::RESET() << endl;\n        return false;\n    }\n\n\n\n\n\n    //---------------- make collection of 3d 2d points\n    int c = 0;\n    MatrixXd u_normalized = stereogeom->get_K().inverse() * u;\n    MatrixXd ud_normalized = stereogeom->get_K().inverse() * ud;\n    std::vector<Eigen::Vector3d> world_point;\n    std::vector<Eigen::Vector2d> feature_position_ud;\n    std::vector<Eigen::Vector2d> feature_position_u;\n    for( int k=0 ; k<u.cols() ; k++ )\n    {\n        cv::Vec3f _3dpt = _3dImage.at<cv::Vec3f>( (int)u(1,k), (int)u(0,k) );\n        if( _3dpt[2] < 0.1 || _3dpt[2] > 25.  )\n            continue;\n\n        // the 3d point should also project to same 2d points.\n\n\n        c++;\n        #if 0\n        cout << TermColor::RED() << \"---\" << k << \"---\" << TermColor::RESET() << endl;\n        cout << \"ud=\" << ud.col(k).transpose() ;\n        cout << \" <--> \";\n        cout << \"u=\" << u.col(k).transpose() ;\n        cout << \"  3dpt of u=\";\n        cout <<  TermColor::GREEN() << _3dpt[0] << \" \" << _3dpt[1] << \" \" << _3dpt[2] << \" \" << TermColor::RESET();\n\n        // project 3d point with Identity\n        Vector3d X_i = Vector3d( (double) _3dpt[0],(double) _3dpt[1],(double) _3dpt[2] );\n        X_i(0) /= X_i(2);\n        X_i(1) /= X_i(2);\n        X_i(2) /= X_i(2);\n        Vector3d u_cap = stereogeom->get_K() * X_i;\n        cout << \"\\nPI(3dpt)=\" << u_cap.transpose() ;\n\n        auto delta = u_cap - u.col(k);\n        if( abs(delta(0))  < 2. && abs(delta(1)) < 2. ) {\n            cout << TermColor::GREEN() << \"\\tdelta=\" << delta.transpose() << TermColor::RESET();\n            make_n_good++;\n        }\n        else\n            cout << \"\\tdelta=\" << delta.transpose() ;\n\n        cout << endl;\n        #endif\n\n        feature_position_ud.push_back( Vector2d( ud_normalized(0,k), ud_normalized(1,k) ) );\n        feature_position_u.push_back( Vector2d( u_normalized(0,k), u_normalized(1,k) ) );\n\n        world_point.push_back( Vector3d( _3dpt[0], _3dpt[1], _3dpt[2] ) );\n        // world_point.push_back( X_i );\n    }\n    cout << TermColor::GREEN() << \"of the total \" << u.cols() << \" point feature correspondences \" << c << \" had valid depths\" << TermColor::RESET() << endl;\n    if( c < 30 ) {\n        cout << TermColor::RED() << \"too few valid 3d points between \" << frame_a << \" and \" << frame_b << TermColor::RESET() << endl;\n        return false;\n    }\n\n\n    #if 1\n    cv::Mat dst_feat_matches;\n    string msg = string(\"gms_matcher;\")+\"of the total \"+std::to_string(u.cols())+\" point feature correspondences \" +std::to_string(c)+ \" had valid depths\";\n    MiscUtils::plot_point_pair( a_imleft_srectified, u, frame_a,\n                     b_imleft_srectified, ud, frame_b,\n                        dst_feat_matches, 3, msg\n                         );\n    cv::Mat dst_feat_matches_resized;\n    cv::resize(dst_feat_matches, dst_feat_matches_resized, cv::Size(), 0.5, 0.5);\n\n    cv::imshow( \"dst_feat_matches\", dst_feat_matches_resized);\n    #endif\n\n    //------------- TODO assess u, ud, world_point for histogram spreads. a more spread in measurements will give more precise more. Also will be helpful to weedout bad poses\n\n\n    //---------------------------------------------------------------------\n    //------------------ theia pnp\n    //---------------------------------------------------------------------\n\n\n    #if 1\n    // simple DlsPnp()\n    std::vector<Eigen::Quaterniond> solution_rotations;\n    std::vector<Eigen::Vector3d> solution_translations;\n    timer.tic();\n    theia::DlsPnp( feature_position_ud, world_point, &solution_rotations, &solution_translations  );\n    cout << timer.toc_milli() << \" : (ms) : theia::DlsPnp done in\\n\";\n    cout << \"solutions count = \" << solution_rotations.size() << \" \" << solution_translations.size() << endl;\n    if( solution_rotations.size() == 0 ) {\n        cout << TermColor::RED() << \" theia::DlsPnp returns no solution\" << TermColor::RESET() << endl;\n        return false;\n    }\n    Matrix4d b_T_a = Matrix4d::Identity();\n    b_T_a.topLeftCorner(3,3) = solution_rotations[0].toRotationMatrix();\n    b_T_a.col(3).topRows(3) = solution_translations[0];\n    // cout << \"solution_T \" << b_T_a << endl;\n    cout << \"solution_T (b_T_a): \" << PoseManipUtils::prettyprintMatrix4d( b_T_a ) << endl;\n    out_b_T_a = b_T_a;\n    // return true;\n    #endif\n\n\n    #if 1\n    // DlsPnp with ransac\n\n    timer.tic();\n    // prep data\n    vector<CorrespondencePair_3d2d> data_r;\n    for( int i=0 ; i<world_point.size() ; i++ )\n    {\n        CorrespondencePair_3d2d _data;\n        _data.a_X = world_point[i];\n        _data.uv_d = feature_position_ud[i];\n        data_r.push_back( _data );\n    }\n\n    // Specify RANSAC parameters.\n\n    DlsPnpWithRansac dlspnp_estimator;\n    RelativePose best_rel_pose;\n\n    // Set the ransac parameters.\n    theia::RansacParameters params;\n    params.error_thresh = 0.02;\n    params.min_inlier_ratio = 0.90;\n    params.max_iterations = 50;\n    params.min_iterations = 5;\n    params.use_mle = true;\n\n    // Create Ransac object, specifying the number of points to sample to\n    // generate a model estimation.\n    theia::Ransac<DlsPnpWithRansac> ransac_estimator(params, dlspnp_estimator);\n    // theia::LMed<DlsPnpWithRansac> ransac_estimator(params, dlspnp_estimator);\n    // Initialize must always be called!\n    ransac_estimator.Initialize();\n\n    theia::RansacSummary summary;\n    ransac_estimator.Estimate(data_r, &best_rel_pose, &summary);\n    //   cout << \"Line y = \" << best_line.m << \"*x + \" << best_line.b;\n    cout << timer.toc_milli() << \"(ms)!! DlsPnpWithRansac ElapsedTime includes data prep\\n\";\n    cout << \"Ransac:\";\n    // for( int i=0; i<summary.inliers.size() ; i++ )\n        // cout << \"\\t\" << i<<\":\"<< summary.inliers[i];\n    cout << \"\\tnum_iterations=\" << summary.num_iterations;\n    cout << \"\\tconfidence=\" << summary.confidence;\n    cout << endl;\n\n    cout << \"best solution (ransac) : \"<< PoseManipUtils::prettyprintMatrix4d( best_rel_pose.b_T_a ) << endl;\n    b_T_a = best_rel_pose.b_T_a;\n\n    #endif\n\n\n\n\n    // TODO : Verify this computed pose by reprojecting 3d points.\n    cout << \"######################\\n## Verification ##\\n#########################\\n\";\n\n    #if 0\n    //---------------------\n    // --- Experiment-1: project 3d points with identity and plot of A\n    //------------------------\n    MatrixXd _3dpts_of_A_projectedonA = MatrixXd::Zero(3, world_point.size() );\n    MatrixXd _detectedpts_of_A = MatrixXd::Zero(3, world_point.size() );\n    int n_good = 0;\n    for( int i = 0 ; i<world_point.size() ; i++ ) {\n        Vector3d _X_i = Vector3d(world_point[i]);\n        _X_i /= _X_i(2); // Z-division for projection\n        _3dpts_of_A_projectedonA.col(i) = stereogeom->get_K() * _X_i; //< scaling with camera-intrinsic matrix\n\n\n\n        Vector3d _tmp;\n        _tmp << feature_position_u[i], 1.0 ;\n        _detectedpts_of_A.col(i) = stereogeom->get_K() * _tmp;\n\n\n        // cout << \"---\\n\";\n        cout << i << \" ) \";\n        cout << \"_3dpts_of_A_projectedonA.col(i)=\"<< _3dpts_of_A_projectedonA.col(i).transpose() << \"\\t\";\n        cout << \"_detectedpts_of_A.col(i)=\" << _detectedpts_of_A.col(i).transpose() ;\n\n        Vector3d delta = _3dpts_of_A_projectedonA.col(i) - _detectedpts_of_A.col(i);\n        if( abs(delta(0)) < 2. && abs(delta(1)) < 2. ) {\n        cout << \"  delta=\" << delta.transpose();\n        n_good++;\n        }\n        else {\n            cout << TermColor::RED() << \"delta=\" << delta.transpose() << TermColor::RESET();\n        }\n        cout << endl;\n   }\n   cout << \"# of points which with ok delta=\" << n_good << \" out of total\" << world_point.size() << endl;\n\n\n    // plot( A, u );\n    cv::Mat __dst;\n    MiscUtils::plot_point_sets( a_imleft_srectified, _detectedpts_of_A, __dst, cv::Scalar( 0,0,255), false );\n    cv::imshow( \"plot( A, u )\", __dst );\n\n\n    // plot( A, _3dpts_of_A_projectedonA )\n    MiscUtils::plot_point_sets( a_imleft_srectified, _3dpts_of_A_projectedonA, __dst, cv::Scalar( 0,255,255), false );\n    cv::imshow( \"plot( A, _3dpts_of_A_projectedonA )\", __dst );\n    #endif\n\n\n    //---------------------------------------------\n    //--- Use the theia's estimated pose do PI( b_T_a * 3dpts ) and plot these\n    //--- points on B. Also plot detected points of B\n    //---------------------------------------------\n    #if 1\n    MatrixXd _3dpts_of_A_projectedonB = MatrixXd::Zero(3, world_point.size() );\n    MatrixXd _detectedpts_of_B = MatrixXd::Zero(3, world_point.size() );\n    MatrixXd _detectedpts_of_A = MatrixXd::Zero(3, world_point.size() );\n\n    int n_good = 0;\n    for( int i=0 ; i<world_point.size() ; i++ ) {\n        Vector4d a_X_i;\n        a_X_i << Vector3d(world_point[i]), 1.0;\n        Vector4d b_X_i = b_T_a * a_X_i;\n        Vector3d _X_i = b_X_i.topRows(3);\n        _X_i /= _X_i(2); // Z-division for projection\n        _3dpts_of_A_projectedonB.col(i) = stereogeom->get_K() * _X_i; //< scaling with camera-intrinsic matrix\n\n\n        Vector3d _tmp;\n        _tmp << feature_position_ud[i], 1.0 ;\n        _detectedpts_of_B.col(i) = stereogeom->get_K() * _tmp;\n\n        _tmp << feature_position_u[i], 1.0 ;\n        _detectedpts_of_A.col(i) = stereogeom->get_K() * _tmp;\n\n\n        Vector3d delta = _3dpts_of_A_projectedonB.col(i) - _detectedpts_of_B.col(i);\n        if( abs(delta(0)) < 2. && abs(delta(1)) < 2. ) {\n            // cout << \"  delta=\" << delta.transpose();\n            n_good++;\n        }\n        else {\n            // cout << TermColor::RED() << \"delta=\" << delta.transpose() << TermColor::RESET();\n        }\n        // cout << endl;\n    }\n    cout << \"n_good=\" <<n_good << endl;\n    #endif\n\n\n    //------------------------------\n    //--- Use relative pose of obometry to do PI( odom_b_T_a * 3dpts ) and plot these on image-b\n    //--- load odometry poses\n    //-------------------------------\n    #if 1\n    MatrixXd _3dpts_of_A_projectedonB_with_odom_rel_pose = MatrixXd::Zero(3, world_point.size() );\n    Matrix4d wTA;\n    RawFileIO::read_eigen_matrix( BASE+\"/\"+std::to_string(frame_a)+\".wTc\", wTA );\n    // cout << \"wTa(odom)\" << PoseManipUtils::prettyprintMatrix4d( wTa ) << endl;\n    Matrix4d wTB;\n    RawFileIO::read_eigen_matrix( BASE+\"/\"+std::to_string(frame_b)+\".wTc\", wTB );\n    // cout << \"wTb(odom)\" << PoseManipUtils::prettyprintMatrix4d( wTb ) << endl;\n    Matrix4d odom_b_T_a = wTB.inverse() * wTA;\n    cout << \"odom_b_T_a\" << PoseManipUtils::prettyprintMatrix4d( odom_b_T_a ) << endl;\n\n    // PI( odom_b_T_a * a_X )\n    for( int i=0 ; i<world_point.size() ; i++ ) {\n        Vector4d a_X_i;\n        a_X_i << Vector3d(world_point[i]), 1.0;\n        Vector4d b_X_i = odom_b_T_a * a_X_i;\n        Vector3d _X_i = b_X_i.topRows(3);\n        _X_i /= _X_i(2); // Z-division for projection\n        _3dpts_of_A_projectedonB_with_odom_rel_pose.col(i) = stereogeom->get_K() * _X_i; //< scaling with camera-intrinsic matrix\n    }\n\n    #endif\n\n\n    // plot( B, ud )\n    cv::Mat __dst;\n    MiscUtils::plot_point_sets( b_imleft_srectified, _detectedpts_of_B, __dst, cv::Scalar( 0,0,255), false, \"_detectedpts_of_B (red)\" );\n    // cv::imshow( \"plot( B, ud )\", __dst );\n\n    // plot( B, _3dpts_of_A_projectedonB )\n    MiscUtils::plot_point_sets( __dst, _3dpts_of_A_projectedonB, cv::Scalar( 0,255,255), false, \";_3dpts_of_A_projectedonB, PI( b_T_a * X),(yellow)\" );\n    // MiscUtils::plot_point_sets( b_imleft_srectified, _3dpts_of_A_projectedonB, __dst, cv::Scalar( 0,255,255), false, \"_3dpts_of_A_projectedonB\" );\n\n    MiscUtils::plot_point_sets( __dst, _detectedpts_of_A, cv::Scalar( 255,255,255), false, \";;_detectedpts_of_A(white)\" );\n\n    MiscUtils::plot_point_sets( __dst, _3dpts_of_A_projectedonB_with_odom_rel_pose, cv::Scalar( 255,0,0), false, \";;;_3dpts_of_A_projectedonB_with_odom_rel_pose, PI( odom_b_T_a * X),(blue)\" );\n\n    string status_msg = \"\";\n    status_msg += \"_detectedpts_of_B (red);\";\n    status_msg += \"_3dpts_of_A_projectedonB, PI( b_T_a * X),(yellow);\";\n    status_msg += \"_detectedpts_of_A(white);\";\n    status_msg += \"_3dpts_of_A_projectedonB_with_odom_rel_pose, PI( odom_b_T_a * X),(blue)\";\n    MiscUtils::append_status_image( __dst,  status_msg );\n\n    cv::imshow( \"b_imleft_srectified\", __dst );\n}\n\nvoid plot_vio_poses( json& json_obj, ros::Publisher& pub )\n{\n    int n_max = json_obj[\"DataNodes\"].size();\n    visualization_msgs::Marker cam, cam_text;\n    ros::Rate loop_rate(100);\n    for( int i=0 ; i<n_max ; i++ ) {\n        if(  json_obj[\"DataNodes\"][i][\"isPoseAvailable\"] == 1 ) {\n            // cout << i << \" isPoseAvailable: \" <<  json_obj[\"DataNodes\"][i][\"isPoseAvailable\"] << endl;\n            // Matrix4d w_T_c_from_file;\n            // RawFileIO::read_eigen_matrix( BASE+\"/\"+to_string(i)+\".wTc\", w_T_c_from_file );\n            // cout << \"w_T_c_from_file \"<< w_T_c_from_file << endl;\n\n\n            Matrix4d w_T_c;\n            vector<double> raa = json_obj[\"DataNodes\"][i][\"w_T_c\"][\"data\"];\n            RawFileIO::read_eigen_matrix( raa, w_T_c );//loads as row-major\n            // cout << \"w_T_c \"<< w_T_c << endl;\n            cout << \"wTc : \" << PoseManipUtils::prettyprintMatrix4d( w_T_c ) << endl;\n\n            RosMarkerUtils::init_text_marker( cam_text );\n            cam_text.ns = \"vio_pose_text\";\n            cam_text.id = i;\n            cam_text.text = std::to_string(i);\n            cam_text.scale.z = 0.08;\n            RosMarkerUtils::setcolor_to_marker( 1.0,1.0,1.0, cam_text );\n            RosMarkerUtils::setpose_to_marker( w_T_c, cam_text );\n            pub.publish( cam_text );\n\n\n            RosMarkerUtils::init_camera_marker( cam, 0.7 );\n            cam.ns = \"vio_pose\";\n            cam.id = i;\n            RosMarkerUtils::setpose_to_marker( w_T_c, cam );\n            RosMarkerUtils::setcolor_to_marker( 0.0, 1.0, 0.0, cam );\n            pub.publish( cam );\n            ros::spinOnce();\n            loop_rate.sleep();\n\n        }\n    }\n\n}\n\n\nvoid plot_loop_candidates_as_lines( json& log, json& loopcandidates, ros::Publisher&pub )\n{\n\n    visualization_msgs::Marker loop_line_marker;\n    visualization_msgs::Marker loop_line_marker_new;\n    std::shared_ptr<StereoGeometry> stereogeom = make_stereo_geom();\n\n    ros::Rate loop_rate(100);\n    for( int i=0 ; i<loopcandidates.size(); i++ )\n    {\n        // int global_a = loopcandidates[i][\"global_a\"];\n        // int global_b = loopcandidates[i][\"global_b\"];\n        int global_a = loopcandidates[i][\"global_b\"]; //smaller indx to be called `a`. This inverting was done for pose computation.\n        int global_b = loopcandidates[i][\"global_a\"];\n        cout << TermColor::iGREEN() << i  << \"   global_a=\" << global_a << \"  global_b=\" << global_b << TermColor::RESET() << endl;\n\n        vector<double> r_wta = log[\"DataNodes\"][global_a][\"w_T_c\"][\"data\"];\n        vector<double> r_wtb = log[\"DataNodes\"][global_b][\"w_T_c\"][\"data\"];\n        Matrix4d w_T_a, w_T_b;\n        RawFileIO::read_eigen_matrix( r_wta, w_T_a );\n        RawFileIO::read_eigen_matrix( r_wtb, w_T_b );\n\n\n        RosMarkerUtils::init_line_marker( loop_line_marker, w_T_a.col(3).topRows(3), w_T_b.col(3).topRows(3)  );\n        RosMarkerUtils::setcolor_to_marker( 1.0, 0.0, 0.0, loop_line_marker );\n        loop_line_marker.ns = \"loopcandidates\";\n        loop_line_marker.id = i;\n        pub.publish( loop_line_marker );\n\n\n\n        // PnP\n        Matrix4d b_T_a;\n        bool status = relative_pose_compute_with_theia( stereogeom, global_a,global_b, b_T_a );\n        if( status ) {\n            Matrix4d w_Tcap_b =  w_T_a * b_T_a.inverse(); //< new pose\n\n            cout << \"w_T_\"<< global_a << \": \" << PoseManipUtils::prettyprintMatrix4d( w_T_a ) << endl;\n            cout << \"w_T_\"<< global_b << \": \" << PoseManipUtils::prettyprintMatrix4d( w_T_b ) << endl;\n            cout << \"w_Tcap_\"<< global_b << \": \" << PoseManipUtils::prettyprintMatrix4d( w_Tcap_b ) << endl; //new pose of b.\n\n            RosMarkerUtils::init_line_marker( loop_line_marker_new, w_T_a.col(3).topRows(3), w_Tcap_b.col(3).topRows(3)  );\n            RosMarkerUtils::setcolor_to_marker( 1.0, 1.0, 1.0, loop_line_marker_new );\n            loop_line_marker_new.ns = \"loopcandidates_corrected\";\n            loop_line_marker_new.id = i;\n            pub.publish( loop_line_marker_new );\n            char key = cv::waitKey(0);\n            if( key == 'q' ) {\n                cout << \"q pressed\\n\";\n                return;\n            }\n        }\n        else {\n            // blue-out the edge which failed geometric verification\n            RosMarkerUtils::setcolor_to_marker( 0.0, 0.0, 1.0, loop_line_marker );\n            pub.publish( loop_line_marker );\n        }\n\n\n\n\n        ros::spinOnce();\n        loop_rate.sleep();\n\n    }\n}\n\n\nbool self_projection_test( std::shared_ptr<StereoGeometry> stereogeom,\n                        int frame_a, int frame_b )\n{\n    // I hacve a doubt that the 3dImage which is created from stereo geometry\n    // gives out the 3d points in co-ordinate system of right camera-center.\n    // I am wanting it in left camera center co-ordinates. Just reproject those\n    // points and ensure my premise.\n\n    //------------------------------------------------\n    //------ 3d points from frame_a\n    //------------------------------------------------\n    cout << TermColor::iGREEN() << \"LOAD: BASE/\"+std::to_string(frame_a)+\".jpg\" << TermColor::RESET() << endl;\n    cv::Mat a_imleft_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_a)+\".jpg\", 0 );\n    cv::Mat a_imright_raw = cv::imread( BASE+\"/\"+std::to_string(frame_a)+\"_1.jpg\", 0 );\n    Matrix4d wTA;\n    RawFileIO::read_eigen_matrix( BASE+\"/\"+std::to_string(frame_a)+\".wTc\", wTA );\n    if( a_imright_raw.empty() || a_imright_raw.empty() )\n    {\n        cout << TermColor::RED() << \"Cannot read image with idx=\" << frame_a << \" perhaps filenotfound\" << TermColor::RESET() << endl;\n        return false;\n    }\n\n\n    cv::Mat a_imleft_srectified, a_imright_srectified;\n    cv::Mat _3dImage;\n    MatrixXd _3dpts;\n    cv::Mat disp_viz;\n    stereogeom->get_srectifiedim_and_3dpoints_and_3dmap_and_disparity_from_raw_images( a_imleft_raw, a_imright_raw,\n        a_imleft_srectified,a_imright_srectified,  _3dpts, _3dImage, disp_viz );\n\n\n\n    //-----------------------------------------------------\n    //--------------- stereo rectify b\n    //-----------------------------------------------------\n    cout << TermColor::iGREEN() << \"LOAD: BASE/\"+std::to_string(frame_b)+\".jpg\" << TermColor::RESET() << endl;\n    cv::Mat b_imleft_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_b)+\".jpg\", 0 );\n    cv::Mat b_imright_raw = cv::imread( BASE+\"/\"+std::to_string(frame_b)+\"_1.jpg\", 0 );\n    Matrix4d wTB;\n    RawFileIO::read_eigen_matrix( BASE+\"/\"+std::to_string(frame_b)+\".wTc\", wTB );\n    if( b_imleft_raw.empty() || b_imright_raw.empty() )\n    {\n        cout << TermColor::RED() << \"Cannot read image with idx=\" << frame_b << \" perhaps filenotfound\" << TermColor::RESET() << endl;\n        return false;\n    }\n    cv::Mat b_imleft_srectified, b_imright_srectified;\n    stereogeom->do_stereo_rectification_of_raw_images( b_imleft_raw, b_imright_raw,\n                            b_imleft_srectified, b_imright_srectified );\n\n\n    //---------------------------------------------------------------------\n    //------------ point matches between a_left, b_left\n    //---------------------------------------------------------------------\n    MatrixXd u, ud; // u is from frame_a; ud is from frame_b\n    point_feature_matches(a_imleft_srectified, b_imleft_srectified, u, ud );\n    if( u.cols() < 30 ) {\n        cout << TermColor::RED() << \"too few gms matches between \" << frame_a << \" and \" << frame_b << TermColor::RESET() << endl;\n        return false;\n    }\n\n\n    //----------------------------------------------------------------------\n    //---------------- make collection of 3d 2d points\n    //----------------------------------------------------------------------\n    int c = 0;\n    MatrixXd u_normalized = stereogeom->get_K().inverse() * u;\n    MatrixXd ud_normalized = stereogeom->get_K().inverse() * ud;\n    std::vector<Eigen::Vector3d> world_point;\n    std::vector<Eigen::Vector2d> feature_position_ud;\n    std::vector<Eigen::Vector2d> feature_position_u;\n    for( int k=0 ; k<u.cols() ; k++ )\n    {\n        cv::Vec3f _3dpt = _3dImage.at<cv::Vec3f>( (int)u(1,k), (int)u(0,k) );\n        if( _3dpt[2] < 0.1 || _3dpt[2] > 25.  )\n            continue;\n\n        // the 3d point should also project to same 2d points.\n\n\n        c++;\n        #if 0\n        cout << TermColor::RED() << \"---\" << k << \"---\" << TermColor::RESET() << endl;\n        cout << \"ud=\" << ud.col(k).transpose() ;\n        cout << \" <--> \";\n        cout << \"u=\" << u.col(k).transpose() ;\n        cout << \"  3dpt of u=\";\n        cout <<  TermColor::GREEN() << _3dpt[0] << \" \" << _3dpt[1] << \" \" << _3dpt[2] << \" \" << TermColor::RESET();\n\n        // project 3d point with Identity\n        Vector3d X_i = Vector3d( (double) _3dpt[0],(double) _3dpt[1],(double) _3dpt[2] );\n        X_i(0) /= X_i(2);\n        X_i(1) /= X_i(2);\n        X_i(2) /= X_i(2);\n        Vector3d u_cap = stereogeom->get_K() * X_i;\n        cout << \"\\nPI(3dpt)=\" << u_cap.transpose() ;\n\n        auto delta = u_cap - u.col(k);\n        if( abs(delta(0))  < 2. && abs(delta(1)) < 2. ) {\n            cout << TermColor::GREEN() << \"\\tdelta=\" << delta.transpose() << TermColor::RESET();\n            make_n_good++;\n        }\n        else\n            cout << \"\\tdelta=\" << delta.transpose() ;\n\n        cout << endl;\n        #endif\n\n        feature_position_ud.push_back( Vector2d( ud_normalized(0,k), ud_normalized(1,k) ) );\n        feature_position_u.push_back( Vector2d( u_normalized(0,k), u_normalized(1,k) ) );\n\n        world_point.push_back( Vector3d( _3dpt[0], _3dpt[1], _3dpt[2] ) );\n        // world_point.push_back( X_i );\n    }\n    cout << TermColor::GREEN() << \"of the total \" << u.cols() << \" point feature correspondences \" << c << \" had valid depths\" << TermColor::RESET() << endl;\n\n\n    #if 0\n    // PI( _3dpts )\n    cout << \"_3dpts.shape=\" << _3dpts.rows() << \",\" << _3dpts.cols() << endl;\n    MatrixXd reporj = MatrixXd::Zero( 3, _3dpts.cols() );\n    vector<cv::Scalar> reporj_falsedepthcolors;\n    auto fp_map = FalseColors();\n    for( int i=0 ; i<_3dpts.cols() ; i++ ) {\n        Vector4d a_X = _3dpts.col(i); //< 3d pt\n        reporj_falsedepthcolors.push_back( fp_map.getFalseColor(a_X(2)/10.) );\n        reporj.col(i) = stereogeom->get_K() * (a_X.topRows(3) / a_X(2)); // u <== PI( a_X )\n    }\n\n    cv::Mat __dst;\n    // MiscUtils::plot_point_sets( a_imleft_raw, reporj, __dst, reporj_falsedepthcolors, 0.5, \"PI(_3dpts) on a_imleft_raw\" );\n    MiscUtils::plot_point_sets( a_imright_raw, reporj, __dst, reporj_falsedepthcolors, 0.5, \"PI(_3dpts) on a_imright_raw\" );\n    cv::imshow( \"__dst\", __dst );\n\n    cv::imshow( \"strip\", fp_map.getStrip(30, 300) );\n    cv::waitKey(0);\n    #endif\n\n\n\n    #if 0\n    // PI(world_point) - OK\n    MatrixXd reproj_of_worldpts_on_left_of_A = MatrixXd::Zero(3,world_point.size());\n    for( int i=0 ; i<world_point.size() ; i++ ) {\n        reproj_of_worldpts_on_left_of_A.col(i) = stereogeom->get_K() * (world_point[i] / world_point[i](2) );\n    }\n\n    cv::Mat __dst;\n    MiscUtils::plot_point_sets( a_imleft_raw, reproj_of_worldpts_on_left_of_A, __dst, cv::Scalar(0,0,255), false, \"reproj_of_worldpts_on_left_of_A(red)\" );\n    MiscUtils::plot_point_sets( __dst, u, cv::Scalar(0,255,0), false, \";u plotted on A (in green)\" );\n    cv::imshow( \"__dst\", __dst );\n    cv::waitKey(0);\n    #endif\n\n\n    Matrix4d odom_b_T_a = wTB.inverse() * wTA ;\n\n    Matrix3d rm_R1, rm_R2;\n    cv::cv2eigen( stereogeom->get_rm_R1(), rm_R1 );\n    cv::cv2eigen( stereogeom->get_rm_R2(), rm_R2 );\n    Matrix4d rm_T1, rm_T2;\n    rm_T1 = Matrix4d::Identity();\n    rm_T2 = Matrix4d::Identity();\n    rm_T1.topLeftCorner<3,3>() = rm_R1;\n    rm_T2.topLeftCorner<3,3>() = rm_R2;\n    cout << \"odom_b_T_a\" << PoseManipUtils::prettyprintMatrix4d( odom_b_T_a ) << endl;\n    cout << \"rm_T1\" << PoseManipUtils::prettyprintMatrix4d( rm_T1 ) << endl;\n    cout << \"rm_T2\" << PoseManipUtils::prettyprintMatrix4d( rm_T2 ) << endl;\n\n    cout << \"get_rm_R1\\n\" << stereogeom->get_rm_R1() << endl;\n    cout << \"get_rm_R1\\n\" << rm_R1 << endl;\n    cout << \"---\\n\";\n    cout << \"get_rm_R2\\n\" << stereogeom->get_rm_R2() << endl;\n    cout << \"get_rm_R2\\n\" << rm_R2 << endl;\n\n\n    MatrixXd reproj_of_worldpts_on_srectifiedleft_of_B = MatrixXd::Zero(3,world_point.size());\n    MatrixXd reproj_of_worldpts_on_srectifiedleft_of_B__1 = MatrixXd::Zero(3,world_point.size());\n\n    for( int i=0 ; i<world_point.size() ; i++ ) {\n        Vector4d ad_X;\n        ad_X << world_point[i] , 1.0;\n        Vector4d bd_X = rm_T2.inverse() * odom_b_T_a * rm_T1 * ad_X;\n        // Vector4d bd_X = odom_b_T_a * ad_X;\n        // Vector4d bd_X = rm_T1.inverse() * odom_b_T_a * ad_X;\n        reproj_of_worldpts_on_srectifiedleft_of_B.col(i) =\n            stereogeom->get_K() * (bd_X.topRows(3) / bd_X(2) );\n\n\n        Vector4d bd_X_1 = (rm_T2.inverse() * (odom_b_T_a * rm_T1)) * ad_X;\n        reproj_of_worldpts_on_srectifiedleft_of_B__1.col(i) =\n            stereogeom->get_K() * (bd_X_1.topRows(3) / bd_X_1(2) );\n    }\n    cv::Mat __dst;\n    MiscUtils::plot_point_sets( b_imleft_srectified, ud, __dst, cv::Scalar(0,255,0), false, \"ud plotted on B srectified_left (in green)\" );\n    MiscUtils::plot_point_sets( __dst, reproj_of_worldpts_on_srectifiedleft_of_B, cv::Scalar(0,0,255), false, \";reproj_of_worldpts_on_srectifiedleft_of_B with odom_b_T_a(red)\" );\n    MiscUtils::plot_point_sets( __dst, reproj_of_worldpts_on_srectifiedleft_of_B__1, cv::Scalar(0,128,255), false, \";;reproj_of_worldpts_on_srectifiedleft_of_B__1 with odom_b_T_a(yellow)\" );\n    cv::imshow( \"__dst\", __dst );\n    cv::waitKey(0);\n\n}\n\n//-----------------------------------------------\n//-------- NEW ----------------------------------\n//-----------------------------------------------\n// Given the point feature matches and the 3d image (from disparity map) will return\n// the valid world points and corresponding points.\n// [Input]\n//      uv: 2xN matrix of point-feature in image-a. In image co-ordinates (not normalized image cords)\n//      _3dImage_uv : 3d image from disparity map of image-a. sizeof( _3dImage_uv) === WxHx3\n//      uv_d: 2xN matrix of point-feature in image-b. Note that uv<-->uv_d are correspondences so should of equal sizes\n// [Output]\n//      feature_position_uv : a subset of uv but normalized_image_cordinates\n//      feature_position_uv_d : a subset of uv_d. results in normalized_image_cordinates\n//      world_point : 3d points of uv.\n// [Note]\n//      feature_position_uv \\subset uv. Selects points which have valid depths.\n//      size of output is same for all 3\n//      world points are of uv and in co-ordinate system of camera center of uv (or image-a).\n\nbool make_3d_2d_collection__using__pfmatches_and_disparity( std::shared_ptr<StereoGeometry> stereogeom,\n            const MatrixXd& uv, const cv::Mat& _3dImage_uv,     const MatrixXd& uv_d,\n                            std::vector<Eigen::Vector2d>& feature_position_uv, std::vector<Eigen::Vector2d>& feature_position_uv_d,\n                            std::vector<Eigen::Vector3d>& world_point )\n{\n    assert( (uv.cols() == uv_d.cols() ) && \"[Cerebro::make_3d_2d_collection__using__pfmatches_and_disparity] pf-matches need to be of same length. You provided of different lengths\\n\" );\n    assert( _3dImage_uv.type() == CV_32FC3 );\n\n    if( uv.cols() != uv_d.cols() ) {\n        cout << TermColor::RED() << \"[Cerebro::make_3d_2d_collection__using__pfmatches_and_disparity] pf-matches need to be of same length. You provided of different lengths\\n\" << TermColor::RESET();\n        return false;\n    }\n\n    if( _3dImage_uv.type() != CV_32FC3 && _3dImage_uv.rows <= 0 && _3dImage_uv.cols <= 0  ) {\n        cout << TermColor::RED() << \"[Cerebro::make_3d_2d_collection__using__pfmatches_and_disparity] _3dImage is expected to be of size CV_32FC3\\n\" << TermColor::RESET();\n        return false;\n    }\n\n\n\n    int c = 0;\n    MatrixXd ud_normalized = stereogeom->get_K().inverse() * uv_d;\n    MatrixXd u_normalized = stereogeom->get_K().inverse() * uv;\n    feature_position_uv.clear();\n    feature_position_uv_d.clear();\n    world_point.clear();\n\n    for( int k=0 ; k<uv.cols() ; k++ )\n    {\n        cv::Vec3f _3dpt = _3dImage_uv.at<cv::Vec3f>( (int)uv(1,k), (int)uv(0,k) );\n        if( _3dpt[2] < 0.1 || _3dpt[2] > 25.  )\n            continue;\n\n        c++;\n        #if 0\n        cout << TermColor::RED() << \"---\" << k << \"---\" << TermColor::RESET() << endl;\n        cout << \"ud=\" << ud.col(k).transpose() ;\n        cout << \" <--> \";\n        cout << \"u=\" << u.col(k).transpose() ;\n        cout << \"  3dpt of u=\";\n        cout <<  TermColor::GREEN() << _3dpt[0] << \" \" << _3dpt[1] << \" \" << _3dpt[2] << \" \" << TermColor::RESET();\n        cout << endl;\n        #endif\n\n        feature_position_uv.push_back( Vector2d( u_normalized(0,k), u_normalized(1,k) ) );\n        feature_position_uv_d.push_back( Vector2d( ud_normalized(0,k), ud_normalized(1,k) ) );\n        world_point.push_back( Vector3d( _3dpt[0], _3dpt[1], _3dpt[2] ) );\n    }\n\n        cout << \"[make_3d_2d_collection__using__pfmatches_and_disparity]of the total \" << uv.cols() << \" point feature correspondences \" << c << \" had valid depths\\n\";\n\n    return true;\n\n    if( c < 30 ) {\n        cout << TermColor::RED() << \"too few valid 3d points between frames\" <<  TermColor::RESET() << endl;\n        return false;\n    }\n\n}\n\n// given pf-matches uv<-->ud_d and their _3dImages. returns the 3d point correspondences at points where it is valid\n// uv_X: the 3d points are in frame of ref of camera-uv\n// uvd_Y: these 3d points are in frame of ref of camera-uvd\nbool make_3d_3d_collection__using__pfmatches_and_disparity(\n    const MatrixXd& uv, const cv::Mat& _3dImage_uv,\n    const MatrixXd& uv_d, const cv::Mat& _3dImage_uv_d,\n    vector<Vector3d>& uv_X, vector<Vector3d>& uvd_Y\n)\n{\n    assert( uv.cols() > 0 && uv.cols() == uv_d.cols() );\n    uv_X.clear();\n    uvd_Y.clear();\n\n    // similar to above but should return world_point__uv and world_point__uv_d\n    int c=0;\n    for( int k=0 ; k<uv.cols() ; k++ )\n    {\n        cv::Vec3f uv_3dpt = _3dImage_uv.at<cv::Vec3f>( (int)uv(1,k), (int)uv(0,k) );\n        cv::Vec3f uvd_3dpt = _3dImage_uv_d.at<cv::Vec3f>( (int)uv_d(1,k), (int)uv_d(0,k) );\n\n        if( uv_3dpt[2] < 0.1 || uv_3dpt[2] > 25. || uvd_3dpt[2] < 0.1 || uvd_3dpt[2] > 25.  )\n            continue;\n\n        uv_X.push_back( Vector3d( uv_3dpt[0], uv_3dpt[1], uv_3dpt[2] ) );\n        uvd_Y.push_back( Vector3d( uvd_3dpt[0], uvd_3dpt[1], uvd_3dpt[2] ) );\n        c++;\n\n    }\n    cout << \"[make_3d_3d_collection__using__pfmatches_and_disparity] of the total \" << uv.cols() << \" point feature correspondences \" << c << \" had valid depths\\n\";\n\n}\n\n\n// Theia's ICP\n// [Input]\n//      uv_X: a 3d point cloud expressed in some frame-of-ref, call it frame-of-ref of `uv`\n//      uvd_Y: a 3d point cloud expressed in another frame-of-ref, call it frame-of-ref of `uvd`\n// [Output]\n//      uvd_T_uv: Relative pose between the point clouds\n// [Note]\n//      uv_X <---> uvd_Y\n#define ____P3P_ICP_( msg ) msg;\n// #define ____P3P_ICP_( msg ) ;\nfloat P3P_ICP( const vector<Vector3d>& uv_X, const vector<Vector3d>& uvd_Y,\n    Matrix4d& uvd_T_uv, string & p3p__msg )\n{\n\n    // call this theia::AlignPointCloudsUmeyamaWithWeights\n    // void AlignPointCloudsUmeyamaWithWeights(\n    //     const std::vector<Eigen::Vector3d>& left,\n    //     const std::vector<Eigen::Vector3d>& right,\n    //     const std::vector<double>& weights, Eigen::Matrix3d* rotation,\n    //     Eigen::Vector3d* translation, double* scale)\n    p3p__msg = \"\";\n    Matrix3d ____R;\n    Vector3d ____t; double ___s=1.0;\n\n    ElapsedTime timer;\n    timer.tic();\n\n    #if 0\n    theia::AlignPointCloudsUmeyama( uv_X, uvd_Y, &____R, &____t, &___s ); // all weights = 1. TODO: ideally weights could be proportion to point's Z.\n    // theia::AlignPointCloudsICP( uv_X, uvd_Y, &____R, &____t ); // all weights = 1. TODO: ideally weights could be proportion to point's Z.\n\n    double elapsed_time_p3p = timer.toc_milli();\n\n    ____P3P_ICP_(\n    cout << \"___R:\\n\" << ____R << endl;\n    cout << \"___t: \" << ____t.transpose() << endl;\n    cout << \"___s: \" << ___s << endl;\n    )\n\n    uvd_T_uv = Matrix4d::Identity();\n    uvd_T_uv.topLeftCorner<3,3>() = ____R;\n    uvd_T_uv.col(3).topRows(3) = ____t;\n\n    ____P3P_ICP_(\n    cout << TermColor::GREEN() << \"p3p done in (ms) \" << elapsed_time_p3p << \"  p3p_ICP: {uvd}_T_{uv} : \" << PoseManipUtils::prettyprintMatrix4d( uvd_T_uv ) << TermColor::RESET() << endl;\n    )\n\n\n\n    if( min( ___s, 1.0/___s ) < 0.9 ) {\n        ____P3P_ICP_( cout << TermColor::RED() << \"theia::AlignPointCloudsUmeyama scales doesn't look good;this usually implies that estimation is bad.        scale= \" << ___s << TermColor::RESET() <<  endl; )\n        p3p__msg += \"p3p_ICP: scale=\" +to_string( ___s )+\" {uvd}_T_{uv} : \" + PoseManipUtils::prettyprintMatrix4d( uvd_T_uv );\n        p3p__msg += \"p3p done in (ms)\" + to_string(elapsed_time_p3p)+\";    theia::AlignPointCloudsUmeyama scales doesn't look good, this usually implies that estimation is bad. scale= \" + to_string(___s);\n        // return -1;\n    }\n\n    p3p__msg += \"p3p done in (ms)\" + to_string(elapsed_time_p3p)+\";    p3p_ICP: {uvd}_T_{uv} : \" + PoseManipUtils::prettyprintMatrix4d( uvd_T_uv );\n    p3p__msg += \";weight=\"+to_string( min( ___s, 1.0/___s ) );\n    // return  min( ___s, 1.0/___s );\n    #endif\n\n\n    // with ransac\n    timer.tic();\n    vector<CorrespondencePair_3d3d> data_r;\n    for( int i=0 ; i<uv_X.size() ; i++ )\n    {\n        CorrespondencePair_3d3d _data;\n        _data.a_X = uv_X[i];\n        _data.b_X = uvd_Y[i];\n        data_r.push_back( _data );\n    }\n\n    AlignPointCloudsUmeyamaWithRansac icp_estimator;\n    RelativePose best_rel_pose;\n\n    // set ransac params\n    theia::RansacParameters params;\n    params.error_thresh = 0.1;\n    params.min_inlier_ratio = 0.7;\n    params.max_iterations = 50;\n    params.min_iterations = 5;\n    params.use_mle = true;\n\n    theia::Ransac<AlignPointCloudsUmeyamaWithRansac> ransac_estimator( params, icp_estimator);\n    ransac_estimator.Initialize();\n\n    theia::RansacSummary summary;\n    ransac_estimator.Estimate(data_r, &best_rel_pose, &summary);\n    auto elapsed_time_p3p_ransac = timer.toc_milli();\n\n    uvd_T_uv = Matrix4d::Identity();\n    uvd_T_uv =  best_rel_pose.b_T_a ;\n\n\n    ____P3P_ICP_(\n    cout << TermColor::iGREEN() << \"ICP Ransac:\";\n    // for( int i=0; i<summary.inliers.size() ; i++ )\n        // cout << \"\\t\" << i<<\":\"<< summary.inliers[i];\n    cout << \"\\tnum_iterations=\" << summary.num_iterations;\n    cout << \"\\tconfidence=\" << summary.confidence;\n    cout << endl;\n    cout << \"best solution (ransac icp ) : \"<< PoseManipUtils::prettyprintMatrix4d( best_rel_pose.b_T_a ) << TermColor::RESET() << endl;\n    )\n    p3p__msg += \"ICP Ransac;\";\n    p3p__msg += \"    best solution (b_T_a) : \"+ PoseManipUtils::prettyprintMatrix4d( best_rel_pose.b_T_a ) + \";\";\n    p3p__msg += \"     #iterations=\"+to_string( summary.num_iterations );\n    p3p__msg += \"    confidence=\"+to_string( summary.confidence ) ;\n    p3p__msg += \"    icp_ransac_elapsetime_ms=\"+to_string( elapsed_time_p3p_ransac ) +\";\";\n    return summary.confidence;\n\n}\n\n// Computation of pose given 3d points and imaged points\n// [Input]\n//      w_X: 3d points in co-ordinate system `w`\n//      c_uv_normalized: normalized image co-ordinates of camera c. These are projections of w_X on the camera c\n// [Output]\n//      c_T_w: pose of the camera is actually the inverse of this matrix. Becareful.\n// #define ___P_N_P__( msg ) msg;\n#define ___P_N_P__( msg ) ;\nfloat PNP( const std::vector<Vector3d>& w_X, const std::vector<Vector2d>& c_uv_normalized,\n    Matrix4d& c_T_w,\n    string& pnp__msg )\n{\n    pnp__msg = \"\";\n    ElapsedTime timer;\n    #if 0\n    //--- DlsPnp\n    std::vector<Eigen::Quaterniond> solution_rotations;\n    std::vector<Eigen::Vector3d> solution_translations;\n    timer.tic();\n    theia::DlsPnp( c_uv_normalized, w_X, &solution_rotations, &solution_translations  );\n    auto elapsed_dls_pnp = timer.toc_milli() ;\n    ___P_N_P__(\n    cout << elapsed_dls_pnp << \" : (ms) : theia::DlsPnp done in\\n\";\n    cout << \"solutions count = \" << solution_rotations.size() << \" \" << solution_translations.size() << endl;\n    )\n\n    if( solution_rotations.size() == 0 ) {\n        ___P_N_P__(\n        cout << TermColor::RED() << \" theia::DlsPnp returns no solution\" << TermColor::RESET() << endl;\n        )\n        pnp__msg = \" theia::DlsPnp returns no solution\";\n        return -1.;\n    }\n\n    if( solution_rotations.size() > 1 ) {\n        ___P_N_P__(\n        cout << TermColor::RED() << \" theia::DlsPnp returns multiple solution\" << TermColor::RESET() << endl;\n        )\n        pnp__msg =  \" theia::DlsPnp returns multiple solution\";\n        return -1.;\n    }\n\n    // retrive solution\n    c_T_w = Matrix4d::Identity();\n    c_T_w.topLeftCorner(3,3) = solution_rotations[0].toRotationMatrix();\n    c_T_w.col(3).topRows(3) = solution_translations[0];\n\n    ___P_N_P__(\n    // cout << \"solution_T \" << b_T_a << endl;\n    cout << TermColor::GREEN() << \"DlsPnp (c_T_w): \" << PoseManipUtils::prettyprintMatrix4d( c_T_w ) << TermColor::RESET() << endl;\n    // out_b_T_a = b_T_a;\n    )\n\n    pnp__msg +=  \"DlsPnp (c_T_w): \" + PoseManipUtils::prettyprintMatrix4d( c_T_w ) + \";\";\n    pnp__msg += \"  elapsed_dls_pnp_ms=\"+to_string(elapsed_dls_pnp)+\";\";\n\n    // return 1.0;\n    #endif\n\n\n    #if 1\n    //--- DlsPnpWithRansac\n    timer.tic();\n    // prep data\n    vector<CorrespondencePair_3d2d> data_r;\n    for( int i=0 ; i<w_X.size() ; i++ )\n    {\n        CorrespondencePair_3d2d _data;\n        _data.a_X = w_X[i];\n        _data.uv_d = c_uv_normalized[i];\n        data_r.push_back( _data );\n    }\n\n    // Specify RANSAC parameters.\n\n    DlsPnpWithRansac dlspnp_estimator;\n    RelativePose best_rel_pose;\n\n    // Set the ransac parameters.\n    theia::RansacParameters params;\n    params.error_thresh = 0.02;\n    params.min_inlier_ratio = 0.7;\n    params.max_iterations = 50;\n    params.min_iterations = 5;\n    params.use_mle = true;\n\n    // Create Ransac object, specifying the number of points to sample to\n    // generate a model estimation.\n    theia::Ransac<DlsPnpWithRansac> ransac_estimator(params, dlspnp_estimator);\n    // Initialize must always be called!\n    ransac_estimator.Initialize();\n\n    theia::RansacSummary summary;\n    ransac_estimator.Estimate(data_r, &best_rel_pose, &summary);\n\n    auto elapsed_dls_pnp_ransac=timer.toc_milli();\n    ___P_N_P__(\n    cout << elapsed_dls_pnp_ransac << \"(ms)!! DlsPnpWithRansac ElapsedTime includes data prep\\n\";\n    cout << \"Ransac:\";\n    // for( int i=0; i<summary.inliers.size() ; i++ )\n        // cout << \"\\t\" << i<<\":\"<< summary.inliers[i];\n    cout << \"\\tnum_iterations=\" << summary.num_iterations;\n    cout << \"\\tconfidence=\" << summary.confidence;\n    cout << endl;\n    cout << TermColor::GREEN() << \"best solution (ransac) : \"<< PoseManipUtils::prettyprintMatrix4d( best_rel_pose.b_T_a ) << TermColor::RESET() << endl;\n    )\n    pnp__msg +=  \"DlsPnpWithRansac (best_rel_pose.b_T_a): \" + PoseManipUtils::prettyprintMatrix4d( best_rel_pose.b_T_a ) + \";\";\n    pnp__msg += string(\"    num_iterations=\")+to_string(summary.num_iterations)+\"  confidence=\"+to_string(summary.confidence);\n    pnp__msg += string( \"   elapsed_dls_pnp_ransac (ms)=\")+to_string(elapsed_dls_pnp_ransac)+\";\";\n\n\n    c_T_w = best_rel_pose.b_T_a;\n    return summary.confidence;\n    #endif\n\n\n\n}\n\nbool verified_alignment( std::shared_ptr<StereoGeometry> stereogeom,\n                        int frame_a, int frame_b )\n{\n    // I hacve a doubt that the 3dImage which is created from stereo geometry\n    // gives out the 3d points in co-ordinate system of right camera-center.\n    // I am wanting it in left camera center co-ordinates. Just reproject those\n    // points and ensure my premise.\n\n    //------------------------------------------------\n    //------ 3d points from frame_a\n    //------------------------------------------------\n    cout << TermColor::iGREEN() << \"LOAD: BASE/\"+std::to_string(frame_a)+\".jpg\" << TermColor::RESET() << endl;\n    cv::Mat a_imleft_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_a)+\".jpg\", 0 );\n    cv::Mat a_imright_raw = cv::imread( BASE+\"/\"+std::to_string(frame_a)+\"_1.jpg\", 0 );\n    Matrix4d wTA;\n    RawFileIO::read_eigen_matrix( BASE+\"/\"+std::to_string(frame_a)+\".wTc\", wTA );\n    if( a_imright_raw.empty() || a_imright_raw.empty() )\n    {\n        cout << TermColor::RED() << \"Cannot read image with idx=\" << frame_a << \" perhaps filenotfound\" << TermColor::RESET() << endl;\n        return false;\n    }\n\n\n    cv::Mat a_imleft_srectified, a_imright_srectified;\n    cv::Mat a_3dImage;\n    MatrixXd a_3dpts;\n    cv::Mat a_disp_viz;\n    stereogeom->get_srectifiedim_and_3dpoints_and_3dmap_and_disparity_from_raw_images( a_imleft_raw, a_imright_raw,\n        a_imleft_srectified,a_imright_srectified,  a_3dpts, a_3dImage, a_disp_viz );\n\n\n\n    //-----------------------------------------------------\n    //--------------- stereo rectify b\n    //-----------------------------------------------------\n    cout << TermColor::iGREEN() << \"LOAD: BASE/\"+std::to_string(frame_b)+\".jpg\" << TermColor::RESET() << endl;\n    cv::Mat b_imleft_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_b)+\".jpg\", 0 );\n    cv::Mat b_imright_raw = cv::imread( BASE+\"/\"+std::to_string(frame_b)+\"_1.jpg\", 0 );\n    Matrix4d wTB;\n    RawFileIO::read_eigen_matrix( BASE+\"/\"+std::to_string(frame_b)+\".wTc\", wTB );\n    if( b_imleft_raw.empty() || b_imright_raw.empty() )\n    {\n        cout << TermColor::RED() << \"Cannot read image with idx=\" << frame_b << \" perhaps filenotfound\" << TermColor::RESET() << endl;\n        return false;\n    }\n    cv::Mat b_imleft_srectified, b_imright_srectified;\n    cv::Mat b_3dImage;\n    MatrixXd b_3dpts;\n    cv::Mat b_disp_viz;\n    stereogeom->get_srectifiedim_and_3dpoints_and_3dmap_and_disparity_from_raw_images( b_imleft_raw, b_imright_raw,\n                            b_imleft_srectified, b_imright_srectified,  b_3dpts, b_3dImage, b_disp_viz );\n\n\n    //---------------------------------------------------------------------\n    //------------ point matches between a_left, b_left\n    //---------------------------------------------------------------------\n    MatrixXd uv, uv_d; // u is from frame_a; ud is from frame_b\n    ElapsedTime timer;\n    timer.tic();\n    point_feature_matches(a_imleft_srectified, b_imleft_srectified, uv, uv_d );\n    string msg_pf_matches = to_string( timer.toc_milli() )+\" (ms) elapsed time for point_feature_matches computation\";\n    if( uv.cols() < 30 ) {\n        cout << TermColor::RED() << \"too few gms matches between \" << frame_a << \" and \" << frame_b << TermColor::RESET() << endl;\n        return false;\n    }\n\n\n    #if 1 // plot ppoint-feature matches\n    cv::Mat dst_feat_matches;\n    MiscUtils::plot_point_pair( a_imleft_srectified, uv, frame_a,\n                     b_imleft_srectified, uv_d, frame_b,\n                        dst_feat_matches, 3, msg_pf_matches+\";#pf-matches: \"+to_string( uv.cols() )  );\n    cv::resize(dst_feat_matches, dst_feat_matches, cv::Size(), 0.5, 0.5 );\n    cv::imshow( \"dst_feat_matches\", dst_feat_matches );\n    #endif\n\n    #if 1  // disparty maps of both images\n    cv::Mat dst_disp;\n    MiscUtils::side_by_side( a_disp_viz, b_disp_viz, dst_disp );\n    MiscUtils::append_status_image( dst_disp, \"a=\"+ to_string(frame_a)+\"     b=\"+to_string(frame_b), .8 );\n    cv::resize(dst_disp, dst_disp, cv::Size(), 0.5, 0.5 );\n    #endif\n\n    //\n    // Collect 3d points\n    //\n\n    // Option-A:\n    cout << TermColor::BLUE() << \"Option-A\" << TermColor::RESET() << endl;\n    std::vector<Eigen::Vector2d> feature_position_uv;\n    std::vector<Eigen::Vector2d> feature_position_uv_d;\n    std::vector<Eigen::Vector3d> world_point_uv;\n    make_3d_2d_collection__using__pfmatches_and_disparity( stereogeom, uv, a_3dImage, uv_d,\n                                feature_position_uv, feature_position_uv_d, world_point_uv);\n    Matrix4d op1__b_T_a; string pnp__msg;\n    float pnp_goodness = PNP( world_point_uv, feature_position_uv_d, op1__b_T_a, pnp__msg  );\n    cout << TermColor::YELLOW() << pnp_goodness << \" op1__b_T_a = \" << PoseManipUtils::prettyprintMatrix4d( op1__b_T_a ) << TermColor::RESET() << endl;\n    cout << pnp__msg << endl;\n\n    // Option-B:\n    cout << TermColor::BLUE() << \"Option-B\" << TermColor::RESET() << endl;\n    std::vector<Eigen::Vector3d> world_point_uv_d;\n    make_3d_2d_collection__using__pfmatches_and_disparity( stereogeom, uv_d, b_3dImage, uv,\n                                feature_position_uv_d, feature_position_uv, world_point_uv_d);\n    Matrix4d op2__a_T_b, op2__b_T_a; string pnp__msg_option_B;\n    float pnp_goodness_optioN_B = PNP( world_point_uv_d, feature_position_uv, op2__a_T_b, pnp__msg_option_B  );\n    op2__b_T_a = op2__a_T_b.inverse();\n    cout << TermColor::YELLOW() << pnp_goodness_optioN_B << \" op2__a_T_b = \" << PoseManipUtils::prettyprintMatrix4d( op2__a_T_b ) << TermColor::RESET() << endl;\n    cout << TermColor::YELLOW() << pnp_goodness_optioN_B << \" op2__b_T_a = \" << PoseManipUtils::prettyprintMatrix4d( op2__b_T_a ) << TermColor::RESET() << endl;\n    cout << pnp__msg_option_B << endl;\n\n    // Option-C\n    cout << TermColor::BLUE() << \"Option-C\" << TermColor::RESET() << endl;\n    vector< Vector3d> uv_X;\n    vector< Vector3d> uvd_Y;\n    make_3d_3d_collection__using__pfmatches_and_disparity( uv, a_3dImage, uv_d, b_3dImage,\n        uv_X, uvd_Y );\n    Matrix4d icp_b_T_a; string p3p__msg;\n    float p3p_goodness = P3P_ICP( uv_X, uvd_Y, icp_b_T_a, p3p__msg );\n    cout << TermColor::YELLOW() << p3p_goodness << \" icp_b_T_a = \" << PoseManipUtils::prettyprintMatrix4d( icp_b_T_a ) << TermColor::RESET() << endl;\n    cout << p3p__msg << endl;\n\n\n    #if 1\n    MiscUtils::append_status_image( dst_disp, \"[b_T_a <-- PNP( 3d(a), uv(b))];\"+pnp__msg+\";[a_T_b <-- PNP( 3d(b), uv(a))];\"+pnp__msg_option_B+\";[icp_b_T_a <-- ICP(3d(a), 3d(b))];\"+p3p__msg );\n    cv::imshow( \"dst_disp\", dst_disp );\n    #endif\n\n\n\n    Matrix4d odom_b_T_a = wTB.inverse() * wTA;\n    cout << TermColor::YELLOW() << \"odom_b_T_a = \" << PoseManipUtils::prettyprintMatrix4d( odom_b_T_a ) << TermColor::RESET() << endl;\n\n\n    cout << \"|op1 - op2|\" << PoseManipUtils::prettyprintMatrix4d( op1__b_T_a.inverse() * op2__b_T_a ) << endl;\n    cout << \"|op1 - icp|\" << PoseManipUtils::prettyprintMatrix4d( op1__b_T_a.inverse() * icp_b_T_a ) << endl;\n    cout << \"|op2 - icp|\" << PoseManipUtils::prettyprintMatrix4d( op2__b_T_a.inverse() * icp_b_T_a ) << endl;\n\n\n}\n\nint main0( int argc, char ** argv )\n{\n    std::shared_ptr<StereoGeometry> stereogeom = make_stereo_geom();\n\n    // load ProcessedLoopCandidate.json\n    cout << \"Open JSON : \" << BASE+\"/matching/ProcessedLoopCandidate.json\" << endl;\n    std::ifstream fp(BASE+\"/matching/ProcessedLoopCandidate.json\");\n    json proc_candi_json;\n    fp >> proc_candi_json;\n    for( int i =0 ; i<proc_candi_json.size() ; i++ ) // loop over all the candidates\n    {\n        int a = proc_candi_json[i][\"idx_a\"];\n        int b = proc_candi_json[i][\"idx_b\"];\n        cout << a << \" \" << b << endl;\n        bool status = verified_alignment( stereogeom, a, b );\n        cv::waitKey(0);\n\n    }\n}\n\nint main(int argc, char ** argv)\n{\n    std::shared_ptr<StereoGeometry> stereogeom = make_stereo_geom();\n    // cout << \"exit main\\n\";\n    // return 0;\n\n    // will return false if cannot compute pose\n\n    if( argc != 3 ) {\n        cout << \"[MAIN::INVALID USAGE] I am expecting you to give two numbers in the command line between which you want to compute the pose\\n\";\n        exit(1);\n    }\n    Matrix4d out_b_T_a = Matrix4d::Identity();\n    int a = stoi( argv[1] );\n    int b = stoi( argv[2] );\n    // bool status = relative_pose_compute_with_theia(stereogeom, a, b, out_b_T_a );\n    // bool status = self_projection_test( stereogeom, a, b );\n    bool status = verified_alignment( stereogeom, a, b );\n\n\n    // bool status = relative_pose_compute_with_theia(stereogeom, 195, 3, out_b_T_a );\n    cv::waitKey(0);\n\n    cout << \"####RESULT####\\n\";\n    cout << \"status: \" << status << endl;\n    cout << PoseManipUtils::prettyprintMatrix4d( out_b_T_a ) << endl;\n\n\n}\n\nint main1( int argc, char ** argv )\n{\n\n    ///////// ROS INIT\n    ros::init(argc, argv, \"camera_geometry_inspect_ptcld\" );\n    ros::NodeHandle n;\n    ros::Publisher chatter_pub = n.advertise<visualization_msgs::Marker>(\"chatter\", 1000);\n    ros::Rate loop_rate(10);\n\n\n    //////////// Open `log.json` and `loopcandidates_liverun.json`\n    string json_fname = BASE+\"/log.json\";\n    cout << TermColor::GREEN() << \"Open file: \" << json_fname << TermColor::RESET() <<  endl;\n    std::ifstream json_fileptr(json_fname);\n    json json_obj;\n    json_fileptr >> json_obj;\n    cout << \"Successfully opened file \"<< json_fname << endl;\n\n\n    string json_loops_fname = BASE+\"/loopcandidates_liverun.json\";\n    cout << TermColor::GREEN() << \"Open file: \" << json_loops_fname << TermColor::RESET() <<  endl;\n    std::ifstream json_fileptr_loops(json_loops_fname);\n    json json_obj_loopcandidates;\n    json_fileptr_loops >> json_obj_loopcandidates;\n    cout << \"Successfully opened file \"<< json_loops_fname << endl;\n\n\n    ///////////// plot_vio_poses\n    plot_vio_poses( json_obj, chatter_pub );\n\n    //////////// plot loop candidates\n    plot_loop_candidates_as_lines(  json_obj, json_obj_loopcandidates, chatter_pub );\n\n\n\n    cout << \"ros::spin()\\n. Press CTRL+C to quit\\n\";\n    ros::spin();\n\n\n    ///// stereo geometry\n/*\n    std::shared_ptr<StereoGeometry> stereogeom = make_stereo_geom();\n    int frame_a = 840;\n    int frame_b = 1344;\n    Matrix4d b_T_a;\n    relative_pose_compute_with_theia( stereogeom, frame_a,frame_b, b_T_a );\n    cv::waitKey(0);\n*/\n\n\n\n}\n"
  },
  {
    "path": "src/unittest/unittest_theia_ransac.cpp",
    "content": "#include <iostream>\n#include <string>\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n#include <theia/theia.h>\n\nint main()\n{\n    cout << \"Hello\\n\";\n    // it was convient to edit unittest_theia instead....TODO: remove this file\n}\n"
  },
  {
    "path": "src/utils/CameraGeometry.cpp",
    "content": "#include \"CameraGeometry.h\"\n\nMonoGeometry::MonoGeometry(camodocal::CameraPtr _camera)\n{\n    assert( _camera && \"Abstract Camera is not set. You need to init camera before setting it to geometry clases\" );\n    if( !_camera ) {\n        cout << \"Abstract Camera is not set. You need to init camera before setting it to geometry clases\\n\";\n        exit(10);\n    }\n    this->camera = _camera;\n\n    // set K_new as default\n    GeometryUtils::getK( this->camera, this->K_new );\n    this->new_fx = K_new(0,0); // 375.;\n    this->new_fy = K_new(1,1); //375.;\n    this->new_cx = K_new(0,2); //376.;\n    this->new_cy = K_new(1,2); //240.;\n\n    // make rectification maps. Remember to remake the maps when set_K is called\n    this->camera->initUndistortRectifyMap( map_x, map_y, new_fx, new_fy, cv::Size(0,0), new_cx, new_cy );\n}\n\n\nvoid MonoGeometry::set_K( Matrix3d K )\n{\n    std::lock_guard<std::mutex> lk(m);\n    this->K_new = K;\n    this->new_fx = K_new(0,0); // 375.;\n    this->new_fy = K_new(1,1); //375.;\n    this->new_cx = K_new(0,2); //376.;\n    this->new_cy = K_new(1,2); //240.;\n\n    // once a new K is set will have to recompute maps.\n    this->camera->initUndistortRectifyMap( map_x, map_y,\n                                    new_fx, new_fy, cv::Size(0,0),\n                                    new_cx, new_cy );\n}\n\nvoid MonoGeometry::do_image_undistortion( const cv::Mat& im_raw, cv::Mat & im_undistorted )\n{\n    std::lock_guard<std::mutex> lk(m);\n    cv::remap( im_raw, im_undistorted, map_x, map_y, CV_INTER_LINEAR );\n}\n\n//-------------------------------------------------------------------------------------//\n\nStereoGeometry::StereoGeometry( camodocal::CameraPtr _left_camera,\n                camodocal::CameraPtr _right_camera,\n                const Matrix4d& __right_T_left )\n{\n    if( !_left_camera || !_right_camera )\n    {\n        cout << \"\\nERROR : Abstract stereo Camera is not set. You need to init camera before setting it to geometry clases\\n\";\n        exit(10);\n    }\n    assert( _left_camera && _right_camera  && \"Abstract stereo Camera is not set. You need to init camera before setting it to geometry clases\" );\n\n    this->camera_left = _left_camera;\n    this->camera_right = _right_camera;\n    // this->right_T_left = Matrix4d( __right_T_left ); //stereo extrinsic. relative pose between two pairs.\n    this->set_stereoextrinsic( __right_T_left  ); //stereo extrinsic. relative pose between two pairs.\n\n    // set K_new from left_camera's intrinsic\n    Matrix3d __K_new;\n    GeometryUtils::getK( this->camera_left, __K_new );\n    this->set_K( __K_new );\n\n\n    left_geom = std::make_shared<MonoGeometry>( this->camera_left );\n    right_geom = std::make_shared<MonoGeometry>( this->camera_right );\n    left_geom->set_K( this->K_new );\n    right_geom->set_K( this->K_new ); // it is really important that you have same K_new for each of the images in the stereo-pair, this is not a mistake.\n\n    // stereo rectification maps\n    // theory : http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/FUSIELLO/tutorial.html\n    // make_stereo_rectification_maps() // no need to call this again as it is already called when u either do set_K() or set_stereoextrinsic().\n\n    // init SGBM\n    // TODO Can put in options to the BM algorithm, look at opencv docs to set the options\n    // like blocksize, numDisparities etc etc.\n    bm = cv::StereoBM::create(64,21);\n    sgbm = cv::StereoSGBM::create();\n\n}\n\nvoid StereoGeometry::print_blockmatcher_algo_info()\n{\n    cout << TermColor::RED();\n    cout << \"name = \" << bm->getDefaultName() << endl;\n    cout << \"getNumDisparities=\" << bm->getNumDisparities() << endl;\n    cout << \"getMinDisparity=\" << bm->getMinDisparity() << endl;\n    cout << \"getBlockSize=\" << bm->getBlockSize() << endl;\n\n\n    cout << TermColor::RESET() << endl;\n}\n\n\n\nvoid StereoGeometry::set_stereoextrinsic( Matrix4d __right_T_left )\n{\n    std::lock_guard<std::mutex> lk(m_extrinsics);\n    this->right_T_left = Matrix4d( __right_T_left );\n    make_stereo_rectification_maps();\n}\n\nvoid StereoGeometry::set_stereoextrinsic(Vector4d quat_xyzw, Vector3d tr_xyz )\n{\n    std::lock_guard<std::mutex> lk(m_extrinsics);\n    Matrix4d transform ;  //right_T_left\n    PoseManipUtils::raw_xyzw_to_eigenmat( quat_xyzw, tr_xyz, transform );\n    this->right_T_left = Matrix4d( transform );\n    make_stereo_rectification_maps();\n}\n\nconst Matrix4d& StereoGeometry::get_stereoextrinsic()\n{\n    std::lock_guard<std::mutex> lk(m_extrinsics);\n    return this->right_T_left;\n}\n\nvoid StereoGeometry::fundamentalmatrix_from_stereoextrinsic( Matrix3d& F )\n{\n    Matrix3d Tx, _Rot;\n    {\n    std::lock_guard<std::mutex> lk2(m_extrinsics);\n    PoseManipUtils::vec_to_cross_matrix( right_T_left.col(3).topRows(3), Tx );\n    _Rot = right_T_left.topLeftCorner(3,3);\n    }\n\n    // The actual formula is : inverse(K_right)' * Tx * R * inverse(K_left)\n    // but we use the same K_new for both. This is a subtle point here.\n    F = this->get_K().transpose().inverse() * Tx * _Rot * this->get_K().inverse();  //< Fundamental Matrix\n}\n\n\nvoid StereoGeometry::draw_epipolarlines( cv::Mat& imleft_undistorted, cv::Mat& imright_undistorted )\n{\n    IOFormat numpyFmt(FullPrecision, 0, \", \", \",\\n\", \"[\", \"]\", \"[\", \"]\");\n\n    // if image is 1 channel make it to 3 channel so that I can plot colored lines and points.\n    cv::Mat imleft_undistorted_3chnl, imright_undistorted_3chnl;\n    if( imleft_undistorted.channels() == 1 )\n        cv::cvtColor(imleft_undistorted, imleft_undistorted_3chnl, CV_GRAY2BGR );\n    else\n        imleft_undistorted_3chnl = imleft_undistorted;\n\n    if( imright_undistorted.channels() == 1 )\n        cv::cvtColor(imright_undistorted, imright_undistorted_3chnl, CV_GRAY2BGR);\n    else\n        imright_undistorted_3chnl = imright_undistorted;\n\n    imleft_undistorted = imleft_undistorted_3chnl;\n    imright_undistorted = imright_undistorted_3chnl;\n\n\n    // Fundamental Matrix\n    Matrix3d F;\n    this->fundamentalmatrix_from_stereoextrinsic( F );\n    cout << \"[StereoGeometry::draw_epipolarlines]F=\" << F.format(numpyFmt) << endl;\n\n\n\n    // tryout multiple points and get their corresponding epipolar line.\n    for( int i=0 ; i<500; i+=20 ) {\n    #if 1\n    // take a sample point x on left image and find the corresponding line on the right image\n    Vector3d x(1.5*i, i, 1.0);\n    Vector3d ld = F * x;\n    MiscUtils::draw_point( x, imleft_undistorted, cv::Scalar(255,0,0) );\n    MiscUtils::draw_line( ld, imright_undistorted, cv::Scalar(255,0,0) );\n    #endif\n\n    #if 1\n    // take a sample point on right image and find the corresponding line on the left image\n    Vector3d xd(i, i, 1.0);\n    Vector3d l = F.transpose() * xd;\n    MiscUtils::draw_line( l, imleft_undistorted, cv::Scalar(0,0,255) );\n    MiscUtils::draw_point( xd, imright_undistorted, cv::Scalar(0,0,255) );\n    #endif\n    }\n\n}\n\n\nvoid StereoGeometry::draw_srectified_epipolarlines( cv::Mat& imleft_srectified, cv::Mat& imright_srectified )\n{\n    IOFormat numpyFmt(FullPrecision, 0, \", \", \",\\n\", \"[\", \"]\", \"[\", \"]\");\n\n    // if image is 1 channel make it to 3 channel so that I can plot colored lines and points.\n    cv::Mat imleft_srectified_3chnl, imright_srectified_3chnl;\n    if( imleft_srectified.channels() == 1 )\n        cv::cvtColor(imleft_srectified, imleft_srectified_3chnl, CV_GRAY2BGR );\n    else\n        imleft_srectified_3chnl = imleft_srectified;\n\n    if( imright_srectified.channels() == 1 )\n        cv::cvtColor(imright_srectified, imright_srectified_3chnl, CV_GRAY2BGR);\n    else\n        imright_srectified_3chnl = imright_srectified;\n\n    imleft_srectified = imleft_srectified_3chnl;\n    imright_srectified = imright_srectified_3chnl;\n\n\n\n\n    Matrix3d F = rm_fundamental_matrix; //< This was computed by make_stereo_rectification_maps.\n    // this->fundamentalmatrix_from_stereoextrinsic( F );\n    cout << \"[StereoGeometry::draw_srectified_epipolarlines]F=\" << F.format(numpyFmt) << endl;\n\n\n\n    // tryout multiple points and get their corresponding epipolar line.\n    for( int i=0 ; i<500; i+=20 ) {\n    #if 1\n    // take a sample point x on left image and find the corresponding line on the right image\n    Vector3d x(1.5*i, i, 1.0);\n    Vector3d ld = F * x;\n    MiscUtils::draw_point( x, imleft_srectified, cv::Scalar(255,0,0) );\n    MiscUtils::draw_line( ld, imright_srectified, cv::Scalar(255,0,0) );\n    #endif\n\n    #if 1\n    // take a sample point on right image and find the corresponding line on the left image\n    Vector3d xd(i, i, 1.0);\n    Vector3d l = F.transpose() * xd;\n    MiscUtils::draw_line( l, imleft_srectified, cv::Scalar(0,0,255) );\n    MiscUtils::draw_point( xd, imright_srectified, cv::Scalar(0,0,255) );\n    #endif\n    }\n\n}\n\n\nvoid StereoGeometry::set_K( Matrix3d K )\n{\n    std::lock_guard<std::mutex> lk(m_intrinsics);\n\n    this->K_new = K;\n    this->new_fx = K_new(0,0); // 375.;\n    this->new_fy = K_new(1,1); //375.;\n    this->new_cx = K_new(0,2); //376.;\n    this->new_cy = K_new(1,2); //240.;\n    make_stereo_rectification_maps();\n}\n\nvoid StereoGeometry::set_K( float _fx, float _fy, float _cx, float _cy )\n{\n    std::lock_guard<std::mutex> lk(m_intrinsics);\n    this->K_new << _fx, 0.0, _cx,\n                    0.0, _fy, _cy,\n                    0.0, 0.0, 1.0;\n\n    this->new_fx = K_new(0,0); // 375.;\n    this->new_fy = K_new(1,1); //375.;\n    this->new_cx = K_new(0,2); //376.;\n    this->new_cy = K_new(1,2); //240.;\n    make_stereo_rectification_maps();\n}\n\nconst Matrix3d& StereoGeometry::get_K()\n{\n    std::lock_guard<std::mutex> lk(m_intrinsics);\n    return this->K_new;\n}\n\n\n// #define __StereoGeometry___make_stereo_rectification_maps(msg) msg;\n#define __StereoGeometry___make_stereo_rectification_maps(msg) ;\nvoid StereoGeometry::make_stereo_rectification_maps()\n{\n    cv::Size imsize = cv::Size( camera_left->imageWidth(), camera_left->imageHeight());\n\n    IOFormat numpyFmt(FullPrecision, 0, \", \", \",\\n\", \"[\", \"]\", \"[\", \"]\");\n\n\n    // Adopted from : http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/FUSIELLO/node18.html\n    __StereoGeometry___make_stereo_rectification_maps( cout << \"[compute_stereo_rectification_transform]\" << endl );\n\n\n\n\n\n    Matrix3d right_R_left = this->right_T_left.topLeftCorner(3,3);\n    Vector3d right_t_left = this->right_T_left.col(3).topRows(3);\n    cv::Mat R, T;\n    cv::eigen2cv( right_R_left, R );\n    cv::eigen2cv( right_t_left, T );\n    __StereoGeometry___make_stereo_rectification_maps(\n    cout << \"R\"<< R << endl;\n    cout << \"T\" << T << endl;\n    cout << \"this->K_new:  \"<< this->K_new << endl;\n    )\n\n    // Matrix3d K_new_eigen = get_K(); //< this causes some issues. Better not use it.\n    Matrix3d K_new_eigen =  this->K_new;//get_K();\n    __StereoGeometry___make_stereo_rectification_maps(\n        cout << \"K_new_eigen: \" << K_new_eigen << endl;\n    )\n    cv::Mat K_new_cvmat;\n    cv::eigen2cv( K_new_eigen, K_new_cvmat );\n\n    // TODO: If need be write getters for these matrices.\n    // cv::Mat R1, R2;\n    // cv::Mat P1, P2;\n    // cv::Mat Q;\n\n    cv::Mat D;\n    // !! Main Call OpenCV !! //\n    /// The below function does exactly this: http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/FUSIELLO/node18.html\n    // See OpenCVdoc: https://docs.opencv.org/3.4/d9/d0c/group__calib3d.html#ga617b1685d4059c6040827800e72ad2b6\n    cv::stereoRectify( K_new_cvmat, D, K_new_cvmat, D, imsize, R, T,\n                        rm_R1, rm_R2, rm_P1, rm_P2, rm_Q\n                    );\n\n    __StereoGeometry___make_stereo_rectification_maps(\n    cout << \"\\tK_new=\" << cv::format(K_new_cvmat, cv::Formatter::FMT_NUMPY)  << endl;\n    cout << \"\\tR1=\" << cv::format(rm_R1, cv::Formatter::FMT_NUMPY) << endl;\n    cout << \"\\tR2=\" << cv::format(rm_R2, cv::Formatter::FMT_NUMPY) << endl;\n    cout << \"\\tP1=\" << cv::format(rm_P1, cv::Formatter::FMT_NUMPY) << endl;\n    cout << \"\\tP2=\" << cv::format(rm_P2, cv::Formatter::FMT_NUMPY) << endl;\n    cout << \"\\tQ=\" << cv::format(rm_Q, cv::Formatter::FMT_NUMPY) << endl;\n    )\n    // cout << TermColor::RESET();\n\n    // For horizontal stereo :\n    // P2(0,3) := Tx * f, where f = P2(0,0) and Tx is the position of left camera as seen by the right camera. right_T_left.\n    double Tx = rm_P2.at<double>(0,3) / rm_P2.at<double>(0,0);\n    rm_shift = Vector3d( Tx, 0, 0);\n    __StereoGeometry___make_stereo_rectification_maps(cout << \"\\tTx = \" << Tx <<  \" Tx is the position of left camera as seen by the right camera. right_T_left\" << endl);\n    Matrix3d TTx;\n    PoseManipUtils::vec_to_cross_matrix( rm_shift, TTx );\n    rm_fundamental_matrix = K_new.transpose().inverse() * TTx  * K_new.inverse();\n    __StereoGeometry___make_stereo_rectification_maps(cout << \"F_rect=\" << rm_fundamental_matrix.format(numpyFmt) << endl);\n\n\n    // For vertical stereo : TODO\n    // P2(1,3) := Ty * f, where f = P2(0,0) and Tx is the horizontal shift between cameras\n    // double Ty = P2(1,3) / P2(0,0);\n\n    __StereoGeometry___make_stereo_rectification_maps(cout << \"[/compute_stereo_rectification_transform]\" << endl);\n\n\n\n    // !! Make Stereo Rectification Maps !! //\n    __StereoGeometry___make_stereo_rectification_maps(cout << \"[make_rectification_maps]\\n\");\n    cv::initUndistortRectifyMap( K_new_cvmat, D, rm_R1, rm_P1, imsize, CV_32FC1, map1_x, map1_y );\n    cv::initUndistortRectifyMap( K_new_cvmat, D, rm_R2, rm_P2, imsize, CV_32FC1, map2_x, map2_y );\n    __StereoGeometry___make_stereo_rectification_maps(\n    cout << \"\\tmap1_x: \" << MiscUtils::cvmat_info( map1_x ) << endl;\n    cout << \"\\tmap1_y: \" << MiscUtils::cvmat_info( map1_y ) << endl;\n    cout << \"\\tmap2_x: \" << MiscUtils::cvmat_info( map2_x ) << endl;\n    cout << \"\\tmap2_y: \" << MiscUtils::cvmat_info( map2_y ) << endl;\n    cout << \"[/make_rectification_maps]\\n\");\n\n}\n\n//-------------------------- Undistort Raw Image ----------------------------------------//\n\nvoid StereoGeometry::do_image_undistortion( const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                            cv::Mat& imleft_undistorted, cv::Mat& imright_undistorted\n                        )\n{\n    std::lock_guard<std::mutex> lk(m_intrinsics);\n    // cout << \"imleft_raw \" << MiscUtils::cvmat_info( imleft_raw ) << endl;\n    // cout << \"imright_raw \" << MiscUtils::cvmat_info( imright_raw ) << endl;\n\n    // this doesn't need a lock as this is not dependent on\n    left_geom->do_image_undistortion( imleft_raw, imleft_undistorted );\n    right_geom->do_image_undistortion( imright_raw, imright_undistorted );\n}\n\n//--------------------------SRectify (Stereo Rectify)----------------------------------------//\n\n\nvoid StereoGeometry::do_stereo_rectification_of_undistorted_images(\n    const cv::Mat& imleft_undistorted, const cv::Mat& imright_undistorted,\n    cv::Mat& imleft_srectified, cv::Mat& imright_srectified )\n{\n    cv::remap( imleft_undistorted, imleft_srectified, this->map1_x, this->map1_y, CV_INTER_LINEAR );\n    cv::remap( imright_undistorted, imright_srectified, this->map2_x, this->map2_y, CV_INTER_LINEAR );\n}\n\n\n\nvoid StereoGeometry::do_stereo_rectification_of_raw_images(\n        const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n        cv::Mat& imleft_srectified, cv::Mat& imright_srectified )\n{\n    cv::Mat imleft_undistorted, imright_undistorted;\n\n    // raw --> undistorted\n    do_image_undistortion(imleft_raw, imright_raw,\n        imleft_undistorted, imright_undistorted\n        );\n\n    // undistorted --> stereo rectify\n    do_stereo_rectification_of_undistorted_images(imleft_undistorted, imright_undistorted,\n        imleft_srectified, imright_srectified\n    );\n}\n\n\n//-----------------------------------DISPARITY Computation--------------------------------------//\n// stereo rectified --> disparity. Uses the stereo-block matching algorithm,\n// ie. cv::StereoBM\n// if you use this function, besure to call `do_stereo_rectification_of_undistorted_images`\n// or `do_stereo_rectification_of_raw_images` on your images before calling this function\nvoid StereoGeometry::do_stereoblockmatching_of_srectified_images(\n    const cv::Mat& imleft_srectified, const cv::Mat& imright_srectified,\n    cv::Mat& disparity\n)\n{\n    // sgbm->compute( imleft_srectified, imright_srectified, disparity ); //disparity is CV_16UC1\n    bm->compute( imleft_srectified, imright_srectified, disparity ); //disparity is CV_16UC1\n    // cout << \"[StereoGeometry::do_stereoblockmatching_of_srectified_images] disparity\" << MiscUtils::cvmat_info( disparity ) << endl;\n}\n\n// raw--> disparity\n// internally does:\n//      step-1: raw-->undistorted\n//      step-2: undistorted--> srectify (stereo rectify)\n//      step-3: srectify --> disparity\nvoid StereoGeometry::do_stereoblockmatching_of_raw_images(\n    const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n    cv::Mat& disparity\n)\n{\n    cv::Mat imleft_srectified, imright_srectified;\n    do_stereo_rectification_of_raw_images( imleft_raw, imright_raw, imleft_srectified, imright_srectified );\n    do_stereoblockmatching_of_srectified_images( imleft_srectified, imright_srectified, disparity );\n}\n\n\n// undistorted --> disparity\n//      step-1: undistorted --> srectified\n//      step-2 : srectified --> disparity\nvoid StereoGeometry::do_stereoblockmatching_of_undistorted_images(\n    const cv::Mat& imleft_undistorted, const cv::Mat& imright_undistorted,\n    cv::Mat& disparity\n)\n{\n    cv::Mat imleft_srectified, imright_srectified;\n    do_stereo_rectification_of_undistorted_images( imleft_undistorted, imright_undistorted, imleft_srectified, imright_srectified );\n    do_stereoblockmatching_of_srectified_images( imleft_srectified, imright_srectified, disparity );\n}\n\n\n\n//----------------------------- DISPARITY and point cloud -------------------------//\n// # Inputs\n//  disparity_raw : The disparity image\n//  fill_eigen_matrix : _3dpts will be allocated and filled only if this flag is true. If this flag is false then eigen matrix is not filled in this function.\n//  make_homogeneous : true will result in Eigen matrix being 4xN else will be 3xN. fill_eigen_matrix need to be true for this to matter.\n// # Outputs\n//  out3d : 3 channel image. X,Y,Z --> ch0, ch1, ch2. Will also contain invalid 3d points.\n//  _3dpts : 4xN matrix containing only valid points. N < disparity_raw.shape[0]*disparity_raw.shape[1].\nvoid StereoGeometry::disparity_to_3DPoints(const cv::Mat& disparity_raw,\n    cv::Mat& out3D, MatrixXd& _3dpts,\n    bool fill_eigen_matrix, bool make_homogeneous )\n{\n    // Adopted from : https://github.com/bkornel/Reproject-Image-To-3D\n    // OpenCV3.3 version has a bug not sure about other version. It produces inf.\n\n    // /\\/\\/\\/\\/\\/\\/\\/\\/\\ ATTEMPT-1 /\\/\\/\\/\\/\\/\\/\\/\\/\\\n    //---------------- assemble all the data needed for computation\n    const cv::Mat Q = this->get_Q();\n    CV_Assert( !disparity_raw.empty() );\n\n    cv::Mat disparity;\n    if( disparity_raw.type() != CV_32FC1 )\n        disparity_raw.convertTo( disparity, CV_32FC1 );\n    else\n        disparity = disparity_raw;\n\n\tCV_Assert(disparity.type() == CV_32FC1 );\n\tCV_Assert( Q.cols == 4 && Q.rows == 4);\n    CV_Assert( Q.type() == CV_32F || Q.type() == CV_64F );\n\n\t// 3-channel matrix for containing the reprojected 3D world coordinates\n\tout3D = cv::Mat::zeros(disparity.size(), CV_32FC3);\n\n\t// Getting the interesting parameters from Q, everything else is zero or one\n    float Q03,Q13,Q23,Q32,Q33;\n\n    if( Q.type() == CV_32F ) {\n    \tQ03 = Q.at<float>(0, 3);\n    \tQ13 = Q.at<float>(1, 3);\n    \tQ23 = Q.at<float>(2, 3);\n    \tQ32 = Q.at<float>(3, 2);\n    \tQ33 = Q.at<float>(3, 3);\n    } else {\n        Q03 = (float) Q.at<double>(0, 3);\n        Q13 = (float) Q.at<double>(1, 3);\n        Q23 = (float) Q.at<double>(2, 3);\n        Q32 = (float) Q.at<double>(3, 2);\n        Q33 = (float) Q.at<double>(3, 3);\n    }\n\n\n    //--------------------- disparity to 3D points store as 3-channel image\n\t// Transforming a single-channel disparity map to a 3-channel image representing a 3D surface\n\tfor (int i = 0; i < disparity.rows; i++)\n\t{\n\t\tconst float* disp_ptr = disparity.ptr<float>(i);\n\t\tcv::Vec3f* out3D_ptr = out3D.ptr<cv::Vec3f>(i);\n\n\t\tfor (int j = 0; j < disparity.cols; j++)\n\t\t{\n\t\t\tconst float pw = 1.0f / (disp_ptr[j]/16. * Q32 + Q33 + 1E-6); // 1E-6 added to avoid inf.\n\n            // ---------------------------------^^^^^\n            // it is crucial that you convert the raw_disparity to\n            // CV_32F and divide by 16. This is because raw_disparity from StereoBM() is in CV_16SC1\n            // (16bit) and a disparity value := NumDisparities*16. so if you have NumDisparities as 64\n            // the maximum value in disparity will be 1024. This is a bit weird. So be careful.\n\n\t\t\tcv::Vec3f& point = out3D_ptr[j];\n\t\t\tpoint[0] = (static_cast<float>(j)+Q03) * pw;\n\t\t\tpoint[1] = (static_cast<float>(i)+Q13) * pw;\n\t\t\tpoint[2] = Q23 * pw;\n\t\t}\n\t}\n\n\n\n    // /\\/\\/\\/\\/\\/\\/\\/\\/\\ ATTEMPT-2 /\\/\\/\\/\\/\\/\\/\\/\\/\n    /*\n    const cv::Mat Q = this->get_Q();\n    cv::Mat Q_32; // opencv's reprojectImageTo3D() need Q to be in CV_32FC1\n    Q.convertTo( Q_32, CV_32F );\n    cv::Mat disparity_32f;\n    disparity_raw.convertTo( disparity_32f, CV_32F );\n    disparity_32f *= (1/16.);\n\n    // if using opencv's reprojectImageTo3D() it is crucial that you convert the raw_disparity to\n    // CV_32F and divide by 16. This is because raw_disparity from StereoBM() is in CV_16SC1\n    // (16bit) and a disparity value := NumDisparities*16. so if you have NumDisparities as 64\n    // the maximum value in disparity will be 1024. This is a bit weird. So be careful.\n\n    cout << \"Q cvinfo\" << MiscUtils::cvmat_info( Q ) << endl;\n    cout << \"Q_32 cvinfo\" << MiscUtils::cvmat_info( Q_32 ) << endl;\n    cout << \"Q\" << Q << endl;\n    cout << (  (Q_32.size() == cv::Size(4,4))?\"true\":\"false\"  )<< endl;\n    cv::reprojectImageTo3D( disparity_32f, out3D, Q_32, true );\n    cv::Mat disparity = disparity_raw;\n    // END /\\/\\/\\/\\/\\/\\/\\/\\/\\ ATTEMPT-2 /\\/\\/\\/\\/\\/\\/\\/\\/\n    */\n\n    if( fill_eigen_matrix == false )\n        return ;\n\n\n    //--------------------------- Eigen 3D points as 3xN or 4xN\n    // TODO: Make this more efficient. If I enable 4xN matrix it takes 8ms/image if I disable it takes 3ms/image.\n    // Step-1: loop over out3D to know how how many were valid points. remove inf points and remove behind points (points where disparity was invalid)\n    int N = 0;\n    for (int i = 0; i < disparity.rows; i++)\n\t{\n\t\tconst float* disp_ptr = disparity.ptr<float>(i);\n\t\tcv::Vec3f* out3D_ptr = out3D.ptr<cv::Vec3f>(i);\n\n\t\tfor (int j = 0; j < disparity.cols; j++)\n\t\t{\n            cv::Vec3f& point = out3D_ptr[j];\n            if( point[2] < 500. && point[2] > 0.0001 ) {\n                N++;\n            }\n        }\n    }\n\n    // Step-2: Allocate Memory\n    if( make_homogeneous )\n    {\n        _3dpts = MatrixXd::Zero( 4, N );\n        // Step-3: Copy values into Eigen::Matrix of shape 4xN.\n        int n=0;\n        for (int i = 0; i < disparity.rows; i++)\n        {\n            const float* disp_ptr = disparity.ptr<float>(i);\n            cv::Vec3f* out3D_ptr = out3D.ptr<cv::Vec3f>(i);\n\n            for (int j = 0; j < disparity.cols; j++)\n            {\n                cv::Vec3f& point = out3D_ptr[j];\n                if( point[2] < 500. && point[2] > 0.0001 ) {\n                    _3dpts(0,n) = (double)point[0];\n                    _3dpts(1,n) = (double)point[1];\n                    _3dpts(2,n) = (double)point[2];\n                    _3dpts(3,n) = (double)1.0;\n                    n++;\n                }\n            }\n        }\n    }\n    else\n    {\n        _3dpts = MatrixXd::Zero( 3, N );\n        // Step-3: Copy values into Eigen::Matrix of shape 4xN.\n        int n=0;\n        for (int i = 0; i < disparity.rows; i++)\n        {\n            const float* disp_ptr = disparity.ptr<float>(i);\n            cv::Vec3f* out3D_ptr = out3D.ptr<cv::Vec3f>(i);\n\n            for (int j = 0; j < disparity.cols; j++)\n            {\n                cv::Vec3f& point = out3D_ptr[j];\n                if( point[2] < 500. && point[2] > 0.0001 ) {\n                    _3dpts(0,n) = (double)point[0];\n                    _3dpts(1,n) = (double)point[1];\n                    _3dpts(2,n) = (double)point[2];\n                    n++;\n                }\n            }\n        }\n    }\n\n\n\n}\n\n\n\n\n// _3dpts : 4xN\n//      1. raw --> srectified\n//      2. srectified --> disparity_raw\n//      3. disparity_raw --> 3d points\n// return only 3d points\nvoid StereoGeometry::get3dpoints_from_raw_images( const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                            MatrixXd& _3dpts    )\n{\n    cv::Mat disp_raw;\n    this->do_stereoblockmatching_of_raw_images( imleft_raw, imright_raw, disp_raw );\n\n\n    const cv::Mat Q = this->get_Q();\n    cv::Mat _3dImage;\n    this->disparity_to_3DPoints( disp_raw, _3dImage, _3dpts, true, true );\n\n    // cout << \"notimplemented\\n\";\n    // exit(11);\n}\n\n\n// returns both 3dpoints and 3dimage\nvoid StereoGeometry::get3dpoints_and_3dmap_from_raw_images( const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                            MatrixXd& _3dpts, cv::Mat& _3dImage     )\n{\n    cv::Mat disp_raw;\n    this->do_stereoblockmatching_of_raw_images( imleft_raw, imright_raw, disp_raw );\n\n\n    const cv::Mat Q = this->get_Q();\n    this->disparity_to_3DPoints( disp_raw, _3dImage, _3dpts, true, true );\n\n}\n\n// returns both 3dpoints and 3dimage along with srectified image pair\nvoid StereoGeometry::get3dpoints_and_3dmap_from_raw_images( const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                            MatrixXd& _3dpts, cv::Mat& _3dImage,\n                        cv::Mat& imleft_srectified, cv::Mat& imright_srectified     )\n{\n    // raw --> srectified\n    this->do_stereo_rectification_of_raw_images(  imleft_raw, imright_raw, imleft_srectified, imright_srectified );\n\n    // srectified --> disp\n    cv::Mat disp_raw;\n    this->do_stereoblockmatching_of_srectified_images( imleft_srectified, imright_srectified, disp_raw );\n\n\n    const cv::Mat Q = this->get_Q();\n    this->disparity_to_3DPoints( disp_raw, _3dImage, _3dpts, true, true );\n\n}\n\n\nvoid StereoGeometry::get3dpoints_and_disparity_from_raw_images( const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                            MatrixXd& _3dpts, cv::Mat& disparity_for_visualization    )\n{\n    cv::Mat disp_raw;\n    this->do_stereoblockmatching_of_raw_images( imleft_raw, imright_raw, disp_raw );\n\n\n    cv::Mat _3dImage;\n    this->disparity_to_3DPoints( disp_raw, _3dImage, _3dpts, true, true );\n\n    cv::Mat disparity_for_visualization_gray;\n    cv::normalize(disp_raw, disparity_for_visualization_gray, 0, 255, CV_MINMAX, CV_8U); //< disp8 used just for visualization\n    cv::applyColorMap(disparity_for_visualization_gray, disparity_for_visualization, cv::COLORMAP_HOT);\n\n\n\n    // cout << \"notimplemented\\n\";\n    // exit(11);\n}\n\n\n\nvoid StereoGeometry::get_srectifiedim_and_3dpoints_and_disparity_from_raw_images(\n                    const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                    cv::Mat& imleft_srectified, cv::Mat& imright_srectified,\n                    MatrixXd& _3dpts, cv::Mat& disparity_for_visualization    )\n{\n    // raw --> srectified\n    this->do_stereo_rectification_of_raw_images( imleft_raw, imright_raw, imleft_srectified,  imright_srectified );\n\n    // block matching\n    cv::Mat disp_raw;\n    this->do_stereoblockmatching_of_srectified_images( imleft_srectified, imright_srectified, disp_raw );\n\n\n    // disparity to 3D points\n    cv::Mat _3dImage;\n    this->disparity_to_3DPoints( disp_raw, _3dImage, _3dpts, true, true );\n\n    cv::Mat disparity_for_visualization_gray;\n    cv::normalize(disp_raw, disparity_for_visualization_gray, 0, 255, CV_MINMAX, CV_8U); //< disp8 used just for visualization\n    cv::applyColorMap(disparity_for_visualization_gray, disparity_for_visualization, cv::COLORMAP_HOT);\n\n\n\n\n}\n\nvoid StereoGeometry::get_srectifiedim_and_3dpoints_and_3dmap_and_disparity_from_raw_images(\n                    const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                    cv::Mat& imleft_srectified, cv::Mat& imright_srectified,\n                    MatrixXd& _3dpts, cv::Mat& _3dImage, cv::Mat& disparity_for_visualization    )\n{\n    // raw --> srectified\n    this->do_stereo_rectification_of_raw_images( imleft_raw, imright_raw, imleft_srectified,  imright_srectified );\n\n    // block matching\n    cv::Mat disp_raw;\n    this->do_stereoblockmatching_of_srectified_images( imleft_srectified, imright_srectified, disp_raw );\n\n\n    // disparity to 3D points\n    this->disparity_to_3DPoints( disp_raw, _3dImage, _3dpts, true, true );\n\n    cv::Mat disparity_for_visualization_gray;\n    cv::normalize(disp_raw, disparity_for_visualization_gray, 0, 255, CV_MINMAX, CV_8U); //< disp8 used just for visualization\n    cv::applyColorMap(disparity_for_visualization_gray, disparity_for_visualization, cv::COLORMAP_HOT);\n\n\n\n\n}\n\n\n// _3dImage: 3d points as 3 channel image. 1st channel is X, 2nd channel is Y and 3rd channel is Z.\n// Also note that these co-ordinates and imleft_raw co-ordinate do not correspond. They correspond to\n// the srectified images. Incase you want to use the color info with these 3d points, this\n// will lead to wrong. You should compute the srectified images for that.\n//      1. raw --> srectified\n//      2. srectified --> disparity_raw\n//      3. disparity_raw --> 3d points\nvoid StereoGeometry::get3dmap_from_raw_images( const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                            cv::Mat& _3dImage )\n{\n    cv::Mat disp_raw;\n    this->do_stereoblockmatching_of_raw_images( imleft_raw, imright_raw, disp_raw );\n\n\n    const cv::Mat Q = this->get_Q();\n    MatrixXd _3dpts;\n    this->disparity_to_3DPoints( disp_raw, _3dImage, _3dpts, false, true );\n\n    // cout << \"notimplemented\\n\";\n    // exit(11);\n}\n\n\n// e_3dImageX, e_3dImageY, e_3dImageZ: cv::split( _3dImage ). same size as imleft_raw.shape.\n// Also note that these co-ordinates and imleft_raw co-ordinate do not correspond. They correspond to\n// the srectified images. Incase you want to use the color info with these 3d points, this\n// will lead to wrong. You should compute the srectified images for that.\n//      1. raw --> srectified\n//      2. srectified --> disparity_raw\n//      3. disparity_raw --> 3d points\nvoid StereoGeometry::get3dmap_from_raw_images( const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                            MatrixXd& e_3dImageX, MatrixXd& e_3dImageY, MatrixXd& e_3dImageZ  )\n{\n    // TODO : perhaps return vector<MatrixXd>\n    cout << \"this is somewhat fragile. I am making a 3 array of matrix and returning it. This is bad. this need to be tested for correctness after this can be used safely.\";\n    exit(11);\n\n    cv::Mat disp_raw;\n    this->do_stereoblockmatching_of_raw_images( imleft_raw, imright_raw, disp_raw );\n\n\n    const cv::Mat Q = this->get_Q();\n    cv::Mat _3dImage;\n    MatrixXd _3dpts;\n    this->disparity_to_3DPoints( disp_raw, _3dImage, _3dpts, false, true );\n\n    cv::Mat _3dImage_XYZ[3];   //destination array\n    cv::split(_3dImage,_3dImage_XYZ);//split source\n    cv::cv2eigen( _3dImage_XYZ[0], e_3dImageX );\n    cv::cv2eigen( _3dImage_XYZ[1], e_3dImageY );\n    cv::cv2eigen( _3dImage_XYZ[2], e_3dImageZ );\n}\n\n\n\n\n//-------------------------------------------------------------------------------------//\nvoid GeometryUtils::make_K( float new_fx, float new_fy, float new_cx, float new_cy, Matrix3d& K )\n{\n    K << new_fx, 0, new_cx,\n              0, new_fy, new_cy,\n              0, 0, 1;\n}\n\n\n\n\n\nvoid GeometryUtils::getK( camodocal::CameraPtr m_cam, Matrix3d& K )\n{\n    Matrix3d K_rect;\n    vector<double> m_camera_params;\n    m_cam->writeParameters( m_camera_params ); // retrive camera params from Abstract Camera.\n    // camodocal::CataCamera::Parameters p();\n    // cout << \"size=\" << m_camera_params.size() << \" ::>\\n\" ;\n    // for( int i=0 ; i<m_camera_params.size() ; i++ ) cout << \"\\t\" << i << \" \" << m_camera_params[i] << endl;\n\n    switch( m_cam->modelType() )\n    {\n        case camodocal::Camera::ModelType::MEI:\n            K_rect << m_camera_params[5], 0, m_camera_params[7],\n                      0, m_camera_params[6], m_camera_params[8],\n                      0, 0, 1;\n            break;\n        case camodocal::Camera::ModelType::PINHOLE:\n            K_rect << m_camera_params[4], 0, m_camera_params[6],\n                      0, m_camera_params[5], m_camera_params[7],\n                      0, 0, 1;\n            break;\n        case camodocal::Camera::ModelType::KANNALA_BRANDT:\n            K_rect << m_camera_params[4], 0, m_camera_params[6],\n                      0, m_camera_params[5], m_camera_params[7],\n                      0, 0, 1;\n            break;\n            default:\n            // TODO: Implement for other models. Look at initUndistortRectifyMap for each of the abstract class.\n            cout << \"[getK] Wrong\\nQuit....\";\n            cout << \"Implement for other models. Look at initUndistortRectifyMap for each of the abstract class.\\n\";\n            exit(10);\n\n    }\n    K = Matrix3d(K_rect);\n}\n\n\n// given a point cloud as 3xN or 4xN matrix, gets colors for each based on the depth\nvoid GeometryUtils::depthColors( const MatrixXd& ptcld, vector<cv::Scalar>& out_colors, double min, double max )\n{\n    assert( ( ptcld.rows() == 3 || ptcld.rows() == 4 ) && \"ptcld need to be either a 3xN or a 4xN matrix\" );\n    assert( ptcld.cols() > 0 );\n\n    cv::Mat colormap_gray = cv::Mat::zeros( 1, 256, CV_8UC1 );\n    for( int i=0 ; i<256; i++ ) colormap_gray.at<uchar>(0,i) = i;\n    cv::Mat colormap_color;\n    cv::applyColorMap(colormap_gray, colormap_color, cv::COLORMAP_HOT);\n\n    if( min < 0 )\n        min = ptcld.row(2).minCoeff();\n    if( max < 0 )\n        max = ptcld.row(2).maxCoeff();\n    double mean = ptcld.row(2).mean();\n    assert( max > min );\n\n    out_colors.clear();\n    for( int k=0 ; k<ptcld.cols() ; k++ ) {\n        int ixx = 256.* ( ptcld(2, k ) - min ) / (max - min);\n\n        if( ixx <= 0 ) ixx = 0;\n        if( ixx > 255 ) ixx = 255;\n\n        cv::Vec3b f = colormap_color.at<cv::Vec3b>(0,  (int)ixx );\n        out_colors.push_back( cv::Scalar(f[0], f[1], f[2]) );\n    }\n\n    // cout << \"min = \" << min << \"   max=\" << max << \"    mean=\" << mean << endl;\n\n}\n\n\n// given a point cloud as 3xN or 4xN matrix, gets colors for each based on the depth.\n// return in out_colors as 3xN\nvoid GeometryUtils::depthColors( const MatrixXd& ptcld, MatrixXd& out_colors, double min, double max )\n{\n    assert( ( ptcld.rows() == 3 || ptcld.rows() == 4 ) && \"ptcld need to be either a 3xN or a 4xN matrix\" );\n    assert( ptcld.cols() > 0 );\n\n    cv::Mat colormap_gray = cv::Mat::zeros( 1, 256, CV_8UC1 );\n    for( int i=0 ; i<256; i++ ) colormap_gray.at<uchar>(0,i) = i;\n    cv::Mat colormap_color;\n    cv::applyColorMap(colormap_gray, colormap_color, cv::COLORMAP_HOT);\n\n    if( min < 0 )\n        min = ptcld.row(2).minCoeff();\n    if( max < 0 )\n        max = ptcld.row(2).maxCoeff();\n    double mean = ptcld.row(2).mean();\n    assert( max > min );\n\n    out_colors = MatrixXd( 3,ptcld.cols() );\n    for( int k=0 ; k<ptcld.cols() ; k++ ) {\n        int ixx = 256.* ( ptcld(2, k ) - min ) / (max - min);\n\n        if( ixx <= 0 ) ixx = 0;\n        if( ixx > 255 ) ixx = 255;\n\n        cv::Vec3b f = colormap_color.at<cv::Vec3b>(0,  (int)ixx );\n\n        // out_colors.push_back( cv::Scalar(f[0], f[1], f[2]) );\n        out_colors( 0, k ) = float(f[2]) / 255.;\n        out_colors( 1, k ) = float(f[1]) / 255.;\n        out_colors( 2, k ) = float(f[0]) / 255.;\n    }\n\n    // cout << \"min = \" << min << \"   max=\" << max << \"    mean=\" << mean << endl;\n\n}\n\n\nvoid GeometryUtils::idealProjection( const Matrix3d& K, const MatrixXd& c_X, MatrixXd& uv  )\n{\n    assert( c_X.rows() == 4 && \"c_X need to be expressed in homogeneous co-ordinates\\n\" );\n    assert( c_X.cols() > 0 );\n\n    // a) c_X = c_X / c_X.row(2). ==> Z divide\n    // b) perspective_proj = c_X.topRows(3)\n    MatrixXd uv_normalized = MatrixXd::Constant( 3, c_X.cols(), 1.0 );\n    uv_normalized.row(0) = c_X.row(0).array() / c_X.row(2).array();\n    uv_normalized.row(1) = c_X.row(1).array() / c_X.row(2).array();\n\n\n    // c) uv = K * c_X\n    uv = K * uv_normalized;\n}\n\n\n\nvoid GeometryUtils::idealProjection( const Matrix3d& K, const vector<Vector3d>& c_X, MatrixXd& uv  )\n{\n    assert( c_X.size() > 0 );\n\n    // a) c_X = c_X / c_X.row(2). ==> Z divide\n    // b) perspective_proj = c_X.topRows(3)\n    MatrixXd uv_normalized = MatrixXd::Constant( 3, c_X.size(), 1.0 );\n    // uv_normalized.row(0) = c_X.row(0).array() / c_X.row(2).array();\n    // uv_normalized.row(1) = c_X.row(1).array() / c_X.row(2).array();\n    for( int i=0 ; i<c_X.size() ; i++ ) {\n        uv_normalized.col(i) = c_X[i] / c_X[i](2);\n    }\n\n\n    // c) uv = K * c_X\n    uv = K * uv_normalized;\n}\n\n\n\nvoid GeometryUtils::idealProjection( const Matrix3d& K, const Matrix4d& w_T_c, const vector<Vector3d>& c_X, MatrixXd& uv  )\n{\n    assert( c_X.size() > 0 );\n    assert( abs(w_T_c(3,3)-1.) < 1.0E-5 );\n\n    // a) c_X = c_X / c_X.row(2). ==> Z divide\n    // b) perspective_proj = c_X.topRows(3)\n    MatrixXd uv_normalized = MatrixXd::Constant( 3, c_X.size(), 1.0 );\n    // uv_normalized.row(0) = c_X.row(0).array() / c_X.row(2).array();\n    // uv_normalized.row(1) = c_X.row(1).array() / c_X.row(2).array();\n    for( int i=0 ; i<c_X.size() ; i++ ) {\n        Vector4d __x;\n        __x << c_X[i], 1.0;\n        __x = w_T_c * __x;\n        uv_normalized.col(i) = __x / __x(2);\n    }\n\n\n    // c) uv = K * c_X\n    uv = K * uv_normalized;\n}\n"
  },
  {
    "path": "src/utils/CameraGeometry.h",
    "content": "#pragma once\n// This has 2 classes\n// A) MonoGeometry\n// B) StereoGeometry\n\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n#include <iostream>\n#include <string>\n\n\n#include \"camodocal/camera_models/Camera.h\"\n#include \"camodocal/camera_models/CameraFactory.h\"\n#include \"camodocal/camera_models/CataCamera.h\"\n#include \"camodocal/camera_models/EquidistantCamera.h\"\n\n#include \"MiscUtils.h\"\n#include \"ElapsedTime.h\"\n#include \"PoseManipUtils.h\"\n#include \"TermColor.h\"\n\n// Threads and threadsafety\n#include <thread>\n#include <mutex>\n#include <atomic>\n\nclass MonoGeometry {\npublic:\n    MonoGeometry() {  }\n\n    MonoGeometry(camodocal::CameraPtr _camera );\n\n    // Set the new intrinsic matrix (optional). By default will use the same as\n    // the abstract camera. but the K matrix is essentially just scaling pixel sizes.\n    // this comes to play in stereopairs and/or motion stereo.\n    void set_K( Matrix3d K );\n\n    // this is just a call to cv::remap with the maps already computed.\n    void do_image_undistortion( const cv::Mat& im_raw, cv::Mat & im_undistorted );\n\nprivate:\n    camodocal::CameraPtr camera;\n    Matrix3d K_new;\n    float new_fx, new_fy, new_cx, new_cy;\n\n    cv::Mat map_x, map_y;\n\n    std::mutex m;\n\n};\n\n\n// There are 3 types of images\n//      im*_raw\n//      im*_undistorted\n//      im*_stereo_rectified\nclass StereoGeometry {\npublic:\n    // right_T_left: extrinsic calibration for stereo pair. The position of left camera as viewed from right camera.\n    StereoGeometry( camodocal::CameraPtr _left_camera,\n                    camodocal::CameraPtr _right_camera,\n                    const Matrix4d& right_T_left\n                );\n\n\n    // intrinsic cam matrix for both cameras. Here it is important to set this\n    // else the block matching computation won't be valid. If this is not set\n    // we will use the matrix for left camera as K_new.\n    // Everytime a new K is set, we have to recompute the maps.\n    void set_K( Matrix3d K );\n    void set_K( float _fx, float _fy, float _cx, float _cy );\n    const Matrix3d& get_K();\n\n    // set extrinsic. THis is thread safe\n    // Everytime a new extrinsic is set, we have to recompute the maps.\n    void set_stereoextrinsic( Matrix4d __right_T_left );\n    void set_stereoextrinsic( Vector4d quat_xyzw, Vector3d tr_xyz );\n    const Matrix4d& get_stereoextrinsic();\n\n    void fundamentalmatrix_from_stereoextrinsic( Matrix3d& F );\n    //TODO write another function to return fundamental matrix of rectified stereo pair.\n\n    // Draw epipolar lines on both views. These images will be overwritten. You need\n    // to give me undistorted image-pairs. You can undistory image pair with\n    // StereoGeometry::do_image_undistortion()\n    void draw_epipolarlines( cv::Mat& imleft_undistorted, cv::Mat& imright_undistorted );\n    void draw_srectified_epipolarlines( cv::Mat& imleft_srectified, cv::Mat& imright_srectified );\n\n\n    void do_image_undistortion( const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                                cv::Mat& imleft_undistorted, cv::Mat& imright_undistorted\n                            );\n\n    // given the undistorted images outputs the stereo-rectified pairs\n    // This is essentially just cv::remap using the stereo-rectification maps\n    void do_stereo_rectification_of_undistorted_images(\n        const cv::Mat& imleft_undistorted, const cv::Mat& imright_undistorted,\n        cv::Mat& imleft_srectified, cv::Mat& imright_srectified );\n\n\n    // Given raw image pair return stereo-rectified pair. This is a 2 step process\n    // raw --> undistorted --> stereo rectified\n    void do_stereo_rectification_of_raw_images(\n        const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n        cv::Mat& imleft_srectified, cv::Mat& imright_srectified );\n\n\n    // stereo rectified --> disparity. Uses the stereo-block matching algorithm,\n    // ie. cv::StereoBM\n    // if you use this function, besure to call `do_stereo_rectification_of_undistorted_images`\n    // or `do_stereo_rectification_of_raw_images` on your images before calling this function.\n    // returned disparity is CV_16SC1. Be cautioned.\n    void do_stereoblockmatching_of_srectified_images(\n        const cv::Mat& imleft_srectified, const cv::Mat& imright_srectified,\n        cv::Mat& disparity\n    );\n\n    // raw--> disparity\n    // internally does:\n    //      step-1: raw-->undistorted\n    //      step-2: undistorted--> srectify (stereo rectify)\n    //      step-3: srectify --> disparity\n    void do_stereoblockmatching_of_raw_images(\n        const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n        cv::Mat& disparity\n    );\n\n\n    // undistorted --> disparity\n    //      step-1: undistorted --> srectified\n    //      step-2 : srectified --> disparity\n    void do_stereoblockmatching_of_undistorted_images(\n        const cv::Mat& imleft_undistorted, const cv::Mat& imright_undistorted,\n        cv::Mat& disparity\n    );\n\n    // # Inputs\n    //  disparity_raw : The disparity image\n    //  fill_eigen_matrix : _3dpts will be allocated and filled only if this flag is true. If this flag is false then eigen matrix is not filled in this function.\n    //  make_homogeneous : true will result in Eigen matrix being 4xN else will be 3xN. fill_eigen_matrix need to be true for this to matter.\n    // # Outputs\n    //  out3d : 3 channel image. X,Y,Z --> ch0, ch1, ch2. Will also contain invalid 3d points.\n    //  _3dpts : 4xN matrix containing only valid points. N < disparity_raw.shape[0]*disparity_raw.shape[1].\n    void disparity_to_3DPoints(const cv::Mat& disparity_raw,\n        cv::Mat& out3D, MatrixXd& _3dpts,\n        bool fill_eigen_matrix=true, bool make_homogeneous=true );\n\n\n    // #Input\n    // imleft_raw, imright_raw : Raw images. As is from the camera.\n    // # Output\n    // _3dpts : 4xN\n    //      1. raw --> srectified\n    //      2. srectified --> disparity_raw\n    //      3. disparity_raw --> 3d points\n    void get3dpoints_from_raw_images( const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                                MatrixXd& _3dpts    );\n\n\n\n\n    // #Input\n    // imleft_raw, imright_raw : Raw images. As is from the camera.\n    // # Output\n    // _3dpts : 4xN\n    //      1. raw --> srectified\n    //      2. srectified --> disparity_raw\n    //      3. disparity_raw --> 3d points\n    // _3dImage: 3d points as 3 channel image. 1st channel is X, 2nd channel is Y and 3rd channel is Z.\n    void get3dpoints_and_3dmap_from_raw_images( const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                                MatrixXd& _3dpts, cv::Mat& _3dImage    );\n\n\n    // returns both 3dpoints and 3dimage along with srectified image pair\n    void get3dpoints_and_3dmap_from_raw_images( const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                                MatrixXd& _3dpts, cv::Mat& _3dImage,\n                            cv::Mat& imleft_srectified, cv::Mat& imright_srectified     );\n\n\n    // #Input\n    // imleft_raw, imright_raw : Raw images. As is from the camera.\n    // # Output\n    // _3dImage: 3d points as 3 channel image. 1st channel is X, 2nd channel is Y and 3rd channel is Z.\n    // Also note that these co-ordinates and imleft_raw co-ordinate do not correspond. They correspond to\n    // the srectified images. Incase you want to use the color info with these 3d points, this\n    // will lead to wrong. You should compute the srectified images for that.\n    //      1. raw --> srectified\n    //      2. srectified --> disparity_raw\n    //      3. disparity_raw --> 3d points\n    void get3dmap_from_raw_images( const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                                cv::Mat& _3dImage );\n\n\n    // #Input\n    // imleft_raw, imright_raw : Raw images. As is from the camera.\n    // # Output\n    // e_3dImageX, e_3dImageY, e_3dImageZ: cv::split( _3dImage ). same size as imleft_raw.shape.\n    // Also note that these co-ordinates and imleft_raw co-ordinate do not correspond. They correspond to\n    // the srectified images. Incase you want to use the color info with these 3d points, this\n    // will lead to wrong. You should compute the srectified images for that.\n    //      1. raw --> srectified\n    //      2. srectified --> disparity_raw\n    //      3. disparity_raw --> 3d points\n    void get3dmap_from_raw_images( const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                                MatrixXd& e_3dImageX, MatrixXd& e_3dImageY, MatrixXd& e_3dImageZ  );\n\n\n\n    // Given raw image pair returns valid 3d points and disparity false color map.\n    // #Input\n    //      imleft_raw, imright_raw : Raw images. As is from the camera.\n    // #Output\n    //      _3dpts : 4xN matrix.\n    //      disparity_for_visualization : false color mapped for visualization only. Don't do any processing with this one.\n    void get3dpoints_and_disparity_from_raw_images( const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                                MatrixXd& _3dpts, cv::Mat& disparity_for_visualization    );\n\n    // Given raw image pair return a) stereo-rectified image pair b) valid 3d points c) disparity false color map\n    void get_srectifiedim_and_3dpoints_and_disparity_from_raw_images(\n                        const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                        cv::Mat& imleft_srectified, cv::Mat& imright_srectified,\n                        MatrixXd& _3dpts, cv::Mat& disparity_for_visualization    );\n    void get_srectifiedim_and_3dpoints_and_3dmap_and_disparity_from_raw_images(\n                        const cv::Mat& imleft_raw, const cv::Mat& imright_raw,\n                        cv::Mat& imleft_srectified, cv::Mat& imright_srectified,\n                        MatrixXd& _3dpts, cv::Mat& _3dImage, cv::Mat& disparity_for_visualization    );\n\n\n    // Some  semi private getters of internal variables\npublic:\n    const cv::Mat& get_Q() const { return rm_Q; }\n    // TODO: (if need be) also have getters from rm_R1, rm_R2, rm_P1, rm_P2, rm_shift, rm_fundamental_matrix.\n    const cv::Mat& get_rm_R1( ) const { return rm_R1; }\n    const cv::Mat& get_rm_R2( ) const { return rm_R2; }\n    const cv::Mat& get_rm_P1( ) const { return rm_P1; }\n    const cv::Mat& get_rm_P2( ) const { return rm_P2; }\n\n    void print_blockmatcher_algo_info();\n\nprivate:\n    camodocal::CameraPtr camera_left, camera_right;\n    Matrix4d right_T_left; ///< extrinsic calibration of the stereopair.\n\n    Matrix3d K_new;\n    float new_fx, new_fy, new_cx, new_cy;\n\n    std::shared_ptr<MonoGeometry> left_geom, right_geom;\n\n    // stereo rectify maps\n    // theory : http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/FUSIELLO/tutorial.html\n\n    mutable std::mutex m_extrinsics;\n    mutable std::mutex m_intrinsics;\n\n    // This function is called everytime you change the intrinsics and extrinsics for\n    // the stereo pair. This outputs the stereo-rectification maps, ie. map1_x, map1_y, map2_x, map2_y\n    // This is not intended to be a public call.\n    // TODO: thread safety\n    void make_stereo_rectification_maps();\n    cv::Mat rm_R1, rm_R2, rm_P1, rm_P2, rm_Q;\n    Vector3d rm_shift; // right_T_left\n    Matrix3d rm_fundamental_matrix; // fundamental matrix after rectification\n    cv::Mat map1_x, map1_y, map2_x, map2_y;\n\n\n    // Stereo Block Matching\n    cv::Ptr<cv::StereoBM> bm;\n    cv::Ptr<cv::StereoSGBM> sgbm;\n\n};\n\nclass GeometryUtils {\npublic:\n    // Given the focal length make a K out of it\n    static void make_K( float new_fx, float new_fy, float new_cx, float new_cy, Matrix3d& K );\n\n    // For a camera gets a K\n    static void getK( camodocal::CameraPtr m_cam, Matrix3d& K );\n\n    // Ideal Projection:\n    // a) c_X = c_X / c_X.row(2). ==> Z divide\n    // b) perspective_proj = c_X.topRows(3)\n    // c) uv = K * c_X\n    // # Input\n    // K : Camera params. K_new\n    // c_X : 3D points expressed in frame of the camera.\n    //          Note that if you have world 3d points you will need to transform\n    //          it to camera co-ordinates before you pass it to this function.\n    // uv : image co-ordinates (xy)\n    static void idealProjection( const Matrix3d& K, const MatrixXd& c_X, MatrixXd& uv  );\n    static void idealProjection( const Matrix3d& K, const vector<Vector3d>& c_X, MatrixXd& uv  );\n    static void idealProjection( const Matrix3d& K, const Matrix4d& w_T_c, const vector<Vector3d>& c_X, MatrixXd& uv  );\n\n\n    // given a point cloud as 3xN or 4xN matrix, gets colors for each based on the depth\n    // min==-1 then we will compute the min from data, else will use whatever was supplied\n    // max==-1 then we will compute the max from data, else will use whatever.\n    static void depthColors( const MatrixXd& ptcld, vector<cv::Scalar>& out_colors, double min=-1, double max=-1 );\n    static void depthColors( const MatrixXd& ptcld, MatrixXd& out_colors, double min=-1, double max=-1 ); // out_colors : 3xN rgb \\in [0,1]\n\n};\n"
  },
  {
    "path": "src/utils/ElapsedTime.h",
    "content": "#pragma once\n\n#include <chrono>\n#include <iostream>\n#include <iomanip>\n#include <string>\n#include <sstream>\n\nclass ElapsedTime\n{\npublic:\n    ElapsedTime() {\n        // start timer\n        this->begin = std::chrono::steady_clock::now();\n    }\n\n    void tic() {\n        // start timer\n        this->begin = std::chrono::steady_clock::now();\n    }\n\n\n    int toc_milli() {\n        std::chrono::steady_clock::time_point end= std::chrono::steady_clock::now();\n        return (int) std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();\n    }\n\n    int toc_micro() {\n        std::chrono::steady_clock::time_point end= std::chrono::steady_clock::now();\n        return (int) std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count();\n    }\n\n    int toc( ) {\n        std::chrono::steady_clock::time_point end= std::chrono::steady_clock::now();\n        return (int) std::chrono::duration_cast<std::chrono::seconds>(end - begin).count();\n    }\n\n\n\nprivate:\n    std::chrono::steady_clock::time_point begin;\n\n};\n\nclass DateAndTime\n{\npublic:\n    static std::string current_date_and_time()\n    {\n        auto now = std::chrono::system_clock::now();\n        auto in_time_t = std::chrono::system_clock::to_time_t(now);\n\n        std::stringstream ss;\n        ss << std::put_time(std::localtime(&in_time_t), \"%Y-%m-%d %X\");\n        return ss.str();\n    }\n\n    // TODO Can make more functions later to get hr, min, date etc. Not priority.\n\n};\n"
  },
  {
    "path": "src/utils/GMSMatcher/gms_matcher.cpp",
    "content": "#include \"gms_matcher.h\"\n\n\n\nint gms_matcher::GetInlierMask(vector<bool> &vbInliers, bool WithScale, bool WithRotation) {\n\n\tint max_inlier = 0;\n\n\tif (!WithScale && !WithRotation)\n\t{\n\t\tSetScale(0);\n\t\tmax_inlier = run(1);\n\t\tvbInliers = mvbInlierMask;\n\t\treturn max_inlier;\n\t}\n\n\tif (WithRotation && WithScale)\n\t{\n\t\tfor (int Scale = 0; Scale < 5; Scale++)\n\t\t{\n\t\t\tSetScale(Scale);\n\t\t\tfor (int RotationType = 1; RotationType <= 8; RotationType++)\n\t\t\t{\n\t\t\t\tint num_inlier = run(RotationType);\n\n\t\t\t\tif (num_inlier > max_inlier)\n\t\t\t\t{\n\t\t\t\t\tvbInliers = mvbInlierMask;\n\t\t\t\t\tmax_inlier = num_inlier;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn max_inlier;\n\t}\n\n\tif (WithRotation && !WithScale)\n\t{\n\t\tSetScale(0);\n\t\tfor (int RotationType = 1; RotationType <= 8; RotationType++)\n\t\t{\n\t\t\tint num_inlier = run(RotationType);\n\n\t\t\tif (num_inlier > max_inlier)\n\t\t\t{\n\t\t\t\tvbInliers = mvbInlierMask;\n\t\t\t\tmax_inlier = num_inlier;\n\t\t\t}\n\t\t}\n\t\treturn max_inlier;\n\t}\n\n\tif (!WithRotation && WithScale)\n\t{\n\t\tfor (int Scale = 0; Scale < 5; Scale++)\n\t\t{\n\t\t\tSetScale(Scale);\n\n\t\t\tint num_inlier = run(1);\n\n\t\t\tif (num_inlier > max_inlier)\n\t\t\t{\n\t\t\t\tvbInliers = mvbInlierMask;\n\t\t\t\tmax_inlier = num_inlier;\n\t\t\t}\n\n\t\t}\n\t\treturn max_inlier;\n\t}\n\n\treturn max_inlier;\n}\n\nvoid gms_matcher::AssignMatchPairs(int GridType) {\n\n\tfor (size_t i = 0; i < mNumberMatches; i++)\n\t{\n\t\tPoint2f &lp = mvP1[mvMatches[i].first];\n\t\tPoint2f &rp = mvP2[mvMatches[i].second];\n\n\t\tint lgidx = mvMatchPairs[i].first = GetGridIndexLeft(lp, GridType);\n\t\tint rgidx = -1;\n\n\t\tif (GridType == 1)\n\t\t{\n\t\t\trgidx = mvMatchPairs[i].second = GetGridIndexRight(rp);\n\t\t}\n\t\telse\n\t\t{\n\t\t\trgidx = mvMatchPairs[i].second;\n\t\t}\n\n\t\tif (lgidx < 0 || rgidx < 0)\tcontinue;\n\n\t\tmMotionStatistics.at<int>(lgidx, rgidx)++;\n\t\tmNumberPointsInPerCellLeft[lgidx]++;\n\t}\n\n}\n\nvoid gms_matcher::VerifyCellPairs(int RotationType) {\n\n\tconst int *CurrentRP = mRotationPatterns[RotationType - 1];\n\n\tfor (int i = 0; i < mGridNumberLeft; i++)\n\t{\n\t\tif (sum(mMotionStatistics.row(i))[0] == 0)\n\t\t{\n\t\t\tmCellPairs[i] = -1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tint max_number = 0;\n\t\tfor (int j = 0; j < mGridNumberRight; j++)\n\t\t{\n\t\t\tint *value = mMotionStatistics.ptr<int>(i);\n\t\t\tif (value[j] > max_number)\n\t\t\t{\n\t\t\t\tmCellPairs[i] = j;\n\t\t\t\tmax_number = value[j];\n\t\t\t}\n\t\t}\n\n\t\tint idx_grid_rt = mCellPairs[i];\n\n\t\tconst int *NB9_lt = mGridNeighborLeft.ptr<int>(i);\n\t\tconst int *NB9_rt = mGridNeighborRight.ptr<int>(idx_grid_rt);\n\n\t\tint score = 0;\n\t\tdouble thresh = 0;\n\t\tint numpair = 0;\n\n\t\tfor (size_t j = 0; j < 9; j++)\n\t\t{\n\t\t\tint ll = NB9_lt[j];\n\t\t\tint rr = NB9_rt[CurrentRP[j] - 1];\n\t\t\tif (ll == -1 || rr == -1)\tcontinue;\n\n\t\t\tscore += mMotionStatistics.at<int>(ll, rr);\n\t\t\tthresh += mNumberPointsInPerCellLeft[ll];\n\t\t\tnumpair++;\n\t\t}\n\n\t\tthresh = THRESH_FACTOR * sqrt(thresh / numpair);\n\n\t\tif (score < thresh)\n\t\t\tmCellPairs[i] = -2;\n\t}\n}\n\nint gms_matcher::run(int RotationType) {\n\n\tmvbInlierMask.assign(mNumberMatches, false);\n\n\t// Initialize Motion Statisctics\n\tmMotionStatistics = Mat::zeros(mGridNumberLeft, mGridNumberRight, CV_32SC1);\n\tmvMatchPairs.assign(mNumberMatches, pair<int, int>(0, 0));\n\n\tfor (int GridType = 1; GridType <= 4; GridType++)\n\t{\n\t\t// initialize\n\t\tmMotionStatistics.setTo(0);\n\t\tmCellPairs.assign(mGridNumberLeft, -1);\n\t\tmNumberPointsInPerCellLeft.assign(mGridNumberLeft, 0);\n\n\t\tAssignMatchPairs(GridType);\n\t\tVerifyCellPairs(RotationType);\n\n\t\t// Mark inliers\n\t\tfor (size_t i = 0; i < mNumberMatches; i++)\n\t\t{\n\t\t\tif (mvMatchPairs[i].first >= 0) {\n\t\t\t\tif (mCellPairs[mvMatchPairs[i].first] == mvMatchPairs[i].second)\n\t\t\t\t{\n\t\t\t\t\tmvbInlierMask[i] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tint num_inlier = sum(mvbInlierMask)[0];\n\treturn num_inlier;\n}\n"
  },
  {
    "path": "src/utils/GMSMatcher/gms_matcher.h",
    "content": "#pragma once\n#include <opencv2/opencv.hpp>\n#include <vector>\n#include <iostream>\n#include <ctime>\nusing namespace std;\nusing namespace cv;\n\n#define THRESH_FACTOR 6\n\n// 8 possible rotation and each one is 3 X 3\nconst int mRotationPatterns[8][9] = {\n\t1,2,3,\n\t4,5,6,\n\t7,8,9,\n\n\t4,1,2,\n\t7,5,3,\n\t8,9,6,\n\n\t7,4,1,\n\t8,5,2,\n\t9,6,3,\n\n\t8,7,4,\n\t9,5,1,\n\t6,3,2,\n\n\t9,8,7,\n\t6,5,4,\n\t3,2,1,\n\n\t6,9,8,\n\t3,5,7,\n\t2,1,4,\n\n\t3,6,9,\n\t2,5,8,\n\t1,4,7,\n\n\t2,3,6,\n\t1,5,9,\n\t4,7,8\n};\n\n// 5 level scales\nconst double mScaleRatios[5] = { 1.0, 1.0 / 2, 1.0 / sqrt(2.0), sqrt(2.0), 2.0 };\n\n\nclass gms_matcher\n{\npublic:\n\t// OpenCV Keypoints & Correspond Image Size & Nearest Neighbor Matches\n\tgms_matcher(const vector<KeyPoint> &vkp1, const Size size1, const vector<KeyPoint> &vkp2, const Size size2, const vector<DMatch> &vDMatches)\n\t{\n\t\t// Input initialize\n\t\tNormalizePoints(vkp1, size1, mvP1);\n\t\tNormalizePoints(vkp2, size2, mvP2);\n\t\tmNumberMatches = vDMatches.size();\n\t\tConvertMatches(vDMatches, mvMatches);\n\n\t\t// Grid initialize\n\t\tmGridSizeLeft = Size(20, 20);\n\t\tmGridNumberLeft = mGridSizeLeft.width * mGridSizeLeft.height;\n\n\t\t// Initialize the neihbor of left grid\n\t\tmGridNeighborLeft = Mat::zeros(mGridNumberLeft, 9, CV_32SC1);\n\t\tInitalizeNiehbors(mGridNeighborLeft, mGridSizeLeft);\n\t};\n\t~gms_matcher() {};\n\nprivate:\n\n\t// Normalized Points\n\tvector<Point2f> mvP1, mvP2;\n\n\t// Matches\n\tvector<pair<int, int> > mvMatches;\n\n\t// Number of Matches\n\tsize_t mNumberMatches;\n\n\t// Grid Size\n\tSize mGridSizeLeft, mGridSizeRight;\n\tint mGridNumberLeft;\n\tint mGridNumberRight;\n\n\t// x\t  : left grid idx\n\t// y      :  right grid idx\n\t// value  : how many matches from idx_left to idx_right\n\tMat mMotionStatistics;\n\n\t//\n\tvector<int> mNumberPointsInPerCellLeft;\n\n\t// Inldex  : grid_idx_left\n\t// Value   : grid_idx_right\n\tvector<int> mCellPairs;\n\n\t// Every Matches has a cell-pair\n\t// first  : grid_idx_left\n\t// second : grid_idx_right\n\tvector<pair<int, int> > mvMatchPairs;\n\n\t// Inlier Mask for output\n\tvector<bool> mvbInlierMask;\n\n\t//\n\tMat mGridNeighborLeft;\n\tMat mGridNeighborRight;\n\npublic:\n\n\t// Get Inlier Mask\n\t// Return number of inliers\n\tint GetInlierMask(vector<bool> &vbInliers, bool WithScale = false, bool WithRotation = false);\n\nprivate:\n\n\t// Normalize Key Points to Range(0 - 1)\n\tvoid NormalizePoints(const vector<KeyPoint> &kp, const Size &size, vector<Point2f> &npts) {\n\t\tconst size_t numP = kp.size();\n\t\tconst int width   = size.width;\n\t\tconst int height  = size.height;\n\t\tnpts.resize(numP);\n\n\t\tfor (size_t i = 0; i < numP; i++)\n\t\t{\n\t\t\tnpts[i].x = kp[i].pt.x / width;\n\t\t\tnpts[i].y = kp[i].pt.y / height;\n\t\t}\n\t}\n\n\t// Convert OpenCV DMatch to Match (pair<int, int>)\n\tvoid ConvertMatches(const vector<DMatch> &vDMatches, vector<pair<int, int> > &vMatches) {\n\t\tvMatches.resize(mNumberMatches);\n\t\tfor (size_t i = 0; i < mNumberMatches; i++)\n\t\t{\n\t\t\tvMatches[i] = pair<int, int>(vDMatches[i].queryIdx, vDMatches[i].trainIdx);\n\t\t}\n\t}\n\n\tint GetGridIndexLeft(const Point2f &pt, int type) {\n\t\tint x = 0, y = 0;\n\n\t\tif (type == 1) {\n\t\t\tx = floor(pt.x * mGridSizeLeft.width);\n\t\t\ty = floor(pt.y * mGridSizeLeft.height);\n\n\t\t\tif (y >= mGridSizeLeft.height || x >= mGridSizeLeft.width){\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\n\t\tif (type == 2) {\n\t\t\tx = floor(pt.x * mGridSizeLeft.width + 0.5);\n\t\t\ty = floor(pt.y * mGridSizeLeft.height);\n\n\t\t\tif (x >= mGridSizeLeft.width || x < 1) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\n\t\tif (type == 3) {\n\t\t\tx = floor(pt.x * mGridSizeLeft.width);\n\t\t\ty = floor(pt.y * mGridSizeLeft.height + 0.5);\n\n\t\t\tif (y >= mGridSizeLeft.height || y < 1) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\n\t\tif (type == 4) {\n\t\t\tx = floor(pt.x * mGridSizeLeft.width + 0.5);\n\t\t\ty = floor(pt.y * mGridSizeLeft.height + 0.5);\n\n\t\t\tif (y >= mGridSizeLeft.height || y < 1 || x >= mGridSizeLeft.width || x < 1) {\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\n\t\treturn x + y * mGridSizeLeft.width;\n\t}\n\n\tint GetGridIndexRight(const Point2f &pt) {\n\t\tint x = floor(pt.x * mGridSizeRight.width);\n\t\tint y = floor(pt.y * mGridSizeRight.height);\n\n\t\treturn x + y * mGridSizeRight.width;\n\t}\n\n\t// Assign Matches to Cell Pairs\n\tvoid AssignMatchPairs(int GridType);\n\n\t// Verify Cell Pairs\n\tvoid VerifyCellPairs(int RotationType);\n\n\t// Get Neighbor 9\n\tvector<int> GetNB9(const int idx, const Size& GridSize) {\n\t\tvector<int> NB9(9, -1);\n\n\t\tint idx_x = idx % GridSize.width;\n\t\tint idx_y = idx / GridSize.width;\n\n\t\tfor (int yi = -1; yi <= 1; yi++)\n\t\t{\n\t\t\tfor (int xi = -1; xi <= 1; xi++)\n\t\t\t{\n\t\t\t\tint idx_xx = idx_x + xi;\n\t\t\t\tint idx_yy = idx_y + yi;\n\n\t\t\t\tif (idx_xx < 0 || idx_xx >= GridSize.width || idx_yy < 0 || idx_yy >= GridSize.height)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tNB9[xi + 4 + yi * 3] = idx_xx + idx_yy * GridSize.width;\n\t\t\t}\n\t\t}\n\t\treturn NB9;\n\t}\n\n\tvoid InitalizeNiehbors(Mat &neighbor, const Size& GridSize) {\n\t\tfor (int i = 0; i < neighbor.rows; i++)\n\t\t{\n\t\t\tvector<int> NB9 = GetNB9(i, GridSize);\n\t\t\tint *data = neighbor.ptr<int>(i);\n\t\t\tmemcpy(data, &NB9[0], sizeof(int) * 9);\n\t\t}\n\t}\n\n\tvoid SetScale(int Scale) {\n\t\t// Set Scale\n\t\tmGridSizeRight.width = mGridSizeLeft.width  * mScaleRatios[Scale];\n\t\tmGridSizeRight.height = mGridSizeLeft.height * mScaleRatios[Scale];\n\t\tmGridNumberRight = mGridSizeRight.width * mGridSizeRight.height;\n\n\t\t// Initialize the neihbor of right grid\n\t\tmGridNeighborRight = Mat::zeros(mGridNumberRight, 9, CV_32SC1);\n\t\tInitalizeNiehbors(mGridNeighborRight, mGridSizeRight);\n\t}\n\n\t// Run\n\tint run(int RotationType);\n};\n"
  },
  {
    "path": "src/utils/MiscUtils.cpp",
    "content": "#include \"MiscUtils.h\"\n\nstring MiscUtils::type2str(int type) {\n  string r;\n\n  uchar depth = type & CV_MAT_DEPTH_MASK;\n  uchar chans = 1 + (type >> CV_CN_SHIFT);\n\n  switch ( depth ) {\n    case CV_8U:  r = \"8U\"; break;\n    case CV_8S:  r = \"8S\"; break;\n    case CV_16U: r = \"16U\"; break;\n    case CV_16S: r = \"16S\"; break;\n    case CV_32S: r = \"32S\"; break;\n    case CV_32F: r = \"32F\"; break;\n    case CV_64F: r = \"64F\"; break;\n    default:     r = \"User\"; break;\n  }\n\n  r += \"C\";\n  r += (chans+'0');\n\n  return r;\n}\n\nstring MiscUtils::cvmat_info( const cv::Mat& mat )\n{\n    std::stringstream buffer;\n    buffer << \"shape=\" << mat.rows << \",\" << mat.cols << \",\" << mat.channels() ;\n    buffer << \"\\t\" << \"dtype=\" << MiscUtils::type2str( mat.type() );\n    return buffer.str();\n}\n\nstring MiscUtils::imgmsg_info(const sensor_msgs::ImageConstPtr &img_msg)\n{\n    std::stringstream buffer;\n    buffer << \"---\\n\";\n    buffer << \"\\theader:\\n\";\n    buffer << \"\\t\\tseq: \" << img_msg->header.seq << endl;\n    buffer << \"\\t\\tstamp: \" << img_msg->header.stamp << endl;\n    buffer << \"\\t\\tframe_id: \" << img_msg->header.frame_id << endl;\n    buffer << \"\\twidth:\" << img_msg->width << endl;\n    buffer << \"\\theight:\" << img_msg->height << endl;\n    buffer << \"\\tencoding:\" << img_msg->encoding << endl;\n    buffer << \"\\tis_bigendian:\" << (int) img_msg->is_bigendian << endl;\n    buffer << \"\\tstep:\" << img_msg->step << endl;\n    buffer << \"\\tdata:\" << \"[...\" << img_msg->step * img_msg->height << \" sized array...]\" << endl;\n\n    return buffer.str();\n\n}\n\nstring MiscUtils::imgmsg_info(const sensor_msgs::Image& img_msg)\n{\n    std::stringstream buffer;\n    buffer << \"---\\n\";\n    buffer << \"\\theader:\\n\";\n    buffer << \"\\t\\tseq: \" << img_msg.header.seq << endl;\n    buffer << \"\\t\\tstamp: \" << img_msg.header.stamp << endl;\n    buffer << \"\\t\\tframe_id: \" << img_msg.header.frame_id << endl;\n    buffer << \"\\twidth:\" << img_msg.width << endl;\n    buffer << \"\\theight:\" << img_msg.height << endl;\n    buffer << \"\\tencoding:\" << img_msg.encoding << endl;\n    buffer << \"\\tis_bigendian:\" << (int) img_msg.is_bigendian << endl;\n    buffer << \"\\tstep:\" << img_msg.step << endl;\n    buffer << \"\\tdata:\" << \"[...\" << img_msg.step * img_msg.height << \" sized array...]\" << endl;\n\n    return buffer.str();\n\n}\n\ncv::Mat MiscUtils::getImageFromMsg(const sensor_msgs::ImageConstPtr &img_msg)\n{\n    cv_bridge::CvImageConstPtr ptr;\n    if (img_msg->encoding == \"8UC1\")\n    {\n        sensor_msgs::Image img;\n        img.header = img_msg->header;\n        img.height = img_msg->height;\n        img.width = img_msg->width;\n        img.is_bigendian = img_msg->is_bigendian;\n        img.step = img_msg->step;\n        img.data = img_msg->data;\n        img.encoding = \"mono8\";\n        ptr = cv_bridge::toCvCopy(img, sensor_msgs::image_encodings::MONO8);\n    }\n    else\n        ptr = cv_bridge::toCvCopy(img_msg, sensor_msgs::image_encodings::MONO8);\n\n    cv::Mat img = ptr->image.clone();\n    return img;\n}\n\nstd::vector<std::string>\nMiscUtils::split( std::string const& original, char separator )\n{\n    std::vector<std::string> results;\n    std::string::const_iterator start = original.begin();\n    std::string::const_iterator end = original.end();\n    std::string::const_iterator next = std::find( start, end, separator );\n    while ( next != end ) {\n        results.push_back( std::string( start, next ) );\n        start = next + 1;\n        next = std::find( start, end, separator );\n    }\n    results.push_back( std::string( start, next ) );\n    return results;\n}\n\nvoid MiscUtils::keypoint_2_eigen( const std::vector<cv::KeyPoint>& kp, MatrixXd& uv, bool make_homogeneous )\n{\n    assert( kp.size() > 0 && \"MiscUtils::keypoint_2_eigen empty kp.\");\n    uv = MatrixXd::Constant( (make_homogeneous?3:2), kp.size(), 1.0 );\n    for( int i=0; i<kp.size() ; i++ )\n    {\n        uv(0,i) = kp[i].pt.x;\n        uv(1,i) = kp[i].pt.y;\n    }\n}\n\nvoid MiscUtils::dmatch_2_eigen( const std::vector<cv::KeyPoint>& kp1, const std::vector<cv::KeyPoint>& kp2,\n                            const std::vector<cv::DMatch> matches,\n                            MatrixXd& M1, MatrixXd& M2,\n                            bool make_homogeneous\n                        )\n{\n    assert( matches.size() > 0 && \"MiscUtils::dmatch_2_eigen DMatch cannot be empty\" );\n    assert( kp1.size() > 0 && kp2.size() > 0 && \"MiscUtils::dmatch_2_eigen keypoints cannot be empty\" );\n\n    M1 = MatrixXd::Constant( (make_homogeneous?3:2), matches.size(), 1.0 );\n    M2 = MatrixXd::Constant( (make_homogeneous?3:2), matches.size(), 1.0 );\n    for( int i=0 ; i<matches.size() ; i++ ) {\n        int queryIdx = matches[i].queryIdx; //kp1\n        int trainIdx = matches[i].trainIdx; //kp2\n        assert( queryIdx >=0 && queryIdx < kp1.size() );\n        assert( trainIdx >=0 && trainIdx < kp2.size() );\n        M1(0,i) = kp1[queryIdx].pt.x;\n        M1(1,i) = kp1[queryIdx].pt.y;\n\n        M2(0,i) = kp2[trainIdx].pt.x;\n        M2(1,i) = kp2[trainIdx].pt.y;\n    }\n}\n\n\n\nvoid MiscUtils::plot_point_sets( const cv::Mat& im, const MatrixXd& pts_set, cv::Mat& dst,\n                                        const cv::Scalar& color, bool enable_keypoint_annotation, const string& msg )\n{\n  MatrixXf pts_set_float;\n  pts_set_float = pts_set.cast<float>();\n\n  cv::Mat pts_set_mat;\n  cv::eigen2cv( pts_set_float, pts_set_mat );\n\n  MiscUtils::plot_point_sets( im, pts_set_mat, dst, color, enable_keypoint_annotation, msg );\n}\n\n// in place manip\nvoid MiscUtils::plot_point_sets( cv::Mat& im, const MatrixXd& pts_set,\n                                        const cv::Scalar& color, bool enable_keypoint_annotation, const string& msg )\n{\n  MatrixXf pts_set_float;\n  pts_set_float = pts_set.cast<float>();\n\n  cv::Mat pts_set_mat;\n  cv::eigen2cv( pts_set_float, pts_set_mat );\n\n  MiscUtils::plot_point_sets( im, pts_set_mat, im, color, enable_keypoint_annotation, msg );\n}\n\n// TODO: call the next function instead of actually doing the work.\nvoid MiscUtils::plot_point_sets( const cv::Mat& im, const cv::Mat& pts_set, cv::Mat& dst, const cv::Scalar& color, bool enable_keypoint_annotation, const string& msg )\n{\n\n  if( im.data == dst.data ) { // this is pointer comparison to know if this operation is inplace\n    //   cout << \"src and dst are same\\n\";\n      // src and dst images are same, so dont copy. just ensure it is a 3 channel image.\n      assert( im.rows > 0 && im.cols > 0 && \"\\n[MiscUtils::plot_point_sets]Image appears to be emoty. cannot plot.\\n\");\n      assert( im.channels() == 3 && dst.channels() == 3 && \"[MiscUtils::plot_point_sets]src and dst image are same physical image in memory. They need to be 3 channel.\" );\n  }\n  else {\n    //   dst = cv::Mat( im.rows, im.cols, CV_8UC3 );\n      if( im.channels() == 1 )\n        cv::cvtColor( im, dst, cv::COLOR_GRAY2BGR );\n      else\n        im.copyTo(dst);\n  }\n\n  // cv::putText( dst, to_string(msg.length()), cv::Point(5,5), cv::FONT_HERSHEY_COMPLEX_SMALL, .95, cv::Scalar(0,255,255) );\n  if( msg.length() > 0 ) {\n    vector<std::string> msg_split;\n    msg_split = MiscUtils::split( msg, ';' );\n    for( int q=0 ; q<msg_split.size() ; q++ )\n      cv::putText( dst, msg_split[q], cv::Point(5,20+20*q), cv::FONT_HERSHEY_COMPLEX_SMALL, .95, cv::Scalar(0,255,255) );\n  }\n\n\n  //pts_set is 2xN\n  cv::Point2d pt;\n  for( int i=0 ; i<pts_set.cols ; i++ )\n  {\n    pt = cv::Point2d(pts_set.at<float>(0,i),pts_set.at<float>(1,i) );\n    cv::circle( dst, pt, 2, color, -1 );\n\n    char to_s[20];\n    sprintf( to_s, \"%d\", i);\n    if( enable_keypoint_annotation ) {\n        // cv::putText( dst, to_s, pt, cv::FONT_HERSHEY_COMPLEX_SMALL, 0.5, cv::Scalar(255,255,255) - color  );\n        cv::putText( dst, to_s, pt, cv::FONT_HERSHEY_COMPLEX_SMALL, 0.5, color  );\n    }\n\n  }\n}\n\n\nvoid MiscUtils::plot_point_sets( cv::Mat& im, const MatrixXd& pts_set,\n                                        const cv::Scalar& color, const VectorXi& annotations, const string& msg )\n{\n    plot_point_sets( im, pts_set, im, color, annotations, msg );\n}\n\n\nvoid MiscUtils::plot_point_sets( cv::Mat& im, const MatrixXd& pts_set, cv::Mat& dst,\n                                        const cv::Scalar& color, const VectorXi& annotations, const string& msg )\n\n{\n  // TODO consider addressof(a) == addressof(b)\n  // dst = im.clone();\n\n\n  assert( im.rows > 0 && im.cols > 0 && \"\\n[MiscUtils::plot_point_sets]Image appears to be emoty. cannot plot.\\n\");\n  assert( pts_set.cols() > 0 && pts_set.cols() == annotations.rows() && \"[MiscUtils::plot_point_sets] VectorXi annotation size must be equal to number of points. If you wish to use the default annotation ie. 0,1,...n use `true` instead. If you do not want annotation use `false`.\" );\n  if( im.data == dst.data ) {\n    //   cout << \"src and dst are same\\n\";\n      // src and dst images are same, so dont copy. just ensure it is a 3 channel image.\n      assert( im.channels() == 3 && dst.channels() == 3 && \"[MiscUtils::plot_point_sets]src and dst image are same physical image in memory. They need to be 3 channel.\" );\n  }\n  else {\n    //   dst = cv::Mat( im.rows, im.cols, CV_8UC3 );\n      if( im.channels() == 1 )\n        cv::cvtColor( im, dst, cv::COLOR_GRAY2BGR );\n      else\n        im.copyTo(dst);\n  }\n\n  // cv::putText( dst, to_string(msg.length()), cv::Point(5,5), cv::FONT_HERSHEY_COMPLEX_SMALL, .95, cv::Scalar(0,255,255) );\n  if( msg.length() > 0 ) {\n    vector<std::string> msg_split;\n    msg_split = MiscUtils::split( msg, ';' );\n    for( int q=0 ; q<msg_split.size() ; q++ )\n      cv::putText( dst, msg_split[q], cv::Point(5,20+20*q), cv::FONT_HERSHEY_COMPLEX_SMALL, .95, cv::Scalar(0,255,255) );\n  }\n\n\n  //pts_set is 2xN\n  cv::Point2d pt;\n  for( int i=0 ; i<pts_set.cols() ; i++ )\n  {\n    // pt = cv::Point2d(pts_set.at<float>(0,i),pts_set.at<float>(1,i) );\n    pt = cv::Point2d( (float)pts_set(0,i), (float)pts_set(1,i) );\n    cv::circle( dst, pt, 2, color, -1 );\n\n    char to_s[20];\n    sprintf( to_s, \"%d\", annotations(i) );\n    cv::putText( dst, to_s, pt, cv::FONT_HERSHEY_COMPLEX_SMALL, 0.5, color  );\n\n\n  }\n}\n\n\nvoid MiscUtils::plot_point_sets( const cv::Mat& im, const MatrixXd& pts_set, cv::Mat& dst,\n                                        vector<cv::Scalar>& color_annotations, float alpha, const string& msg )\n{\n\n      assert( im.rows > 0 && im.cols > 0 && \"\\n[MiscUtils::plot_point_sets]Image appears to be emoty. cannot plot.\\n\");\n      assert( pts_set.cols() > 0 && pts_set.cols() == color_annotations.size() && \"[MiscUtils::plot_point_sets] len of color vector must be equal to number of pts.\\n\" );\n      if( im.data == dst.data ) {\n        //   cout << \"src and dst are same\\n\";\n          // src and dst images are same, so dont copy. just ensure it is a 3 channel image.\n          assert( im.channels() == 3 && dst.channels() == 3 && \"[MiscUtils::plot_point_sets]src and dst image are same physical image in memory. They need to be 3 channel.\" );\n      }\n      else {\n        //   dst = cv::Mat( im.rows, im.cols, CV_8UC3 );\n          if( im.channels() == 1 )\n            cv::cvtColor( im, dst, cv::COLOR_GRAY2BGR );\n          else\n            im.copyTo(dst);\n      }\n\n      // cv::putText( dst, to_string(msg.length()), cv::Point(5,5), cv::FONT_HERSHEY_COMPLEX_SMALL, .95, cv::Scalar(0,255,255) );\n      if( msg.length() > 0 ) {\n        vector<std::string> msg_split;\n        msg_split = MiscUtils::split( msg, ';' );\n        for( int q=0 ; q<msg_split.size() ; q++ )\n          cv::putText( dst, msg_split[q], cv::Point(5,20+20*q), cv::FONT_HERSHEY_COMPLEX_SMALL, .95, cv::Scalar(0,255,255) );\n      }\n\n\n      //pts_set is 2xN\n      cv::Point2d pt;\n      int n_outside_image_domain = 0;\n      for( int i=0 ; i<pts_set.cols() ; i++ )\n      {\n          if(  pts_set(0,i) < 0 || pts_set(0,i) > im.cols  ||  pts_set(1,i) < 0 || pts_set(1,i) > im.rows   ) { // avoid points which are outside\n              n_outside_image_domain++;\n              continue;\n        }\n        pt = cv::Point( (int)pts_set(0,i), (int)pts_set(1,i) );\n\n\n        cv::Scalar _color = color_annotations[i];\n        dst.at< cv::Vec3b >( pt )[0] = (uchar) ( (1.0-alpha)*(float)dst.at< cv::Vec3b >( pt )[0] + (alpha)*(float)_color[0] );\n        dst.at< cv::Vec3b >( pt )[1] = (uchar) ( (1.0-alpha)*(float)dst.at< cv::Vec3b >( pt )[1] + (alpha)*(float)_color[1] );\n        dst.at< cv::Vec3b >( pt )[2] = (uchar) ( (1.0-alpha)*(float)dst.at< cv::Vec3b >( pt )[2] + (alpha)*(float)_color[2] );\n\n        // cv::Vec3d new_color( .5*_orgcolor[0]+.5*_color[0] + .5*_orgcolor[1]+.5*_color[1] + .5*_orgcolor[2]+.5*_color[2] )\n      }\n\n      if( float(n_outside_image_domain)/ float(pts_set.cols() ) > 0.2 ) // print only if u see too many outside the image\n          cout << \"[MiscUtils::plot_point_sets] with color at every point, found \" << n_outside_image_domain << \" outside the image of total points to plot=\" << pts_set.cols() << \"\\n\";\n}\n\n\n\nvoid MiscUtils::plot_point_pair( const cv::Mat& imA, const MatrixXd& ptsA, int idxA,\n                      const cv::Mat& imB, const MatrixXd& ptsB, int idxB,\n                    //   const VectorXd& mask,\n                    cv::Mat& dst,\n                    const cv::Scalar& color_marker,\n                    const cv::Scalar& color_line,\n                      bool annotate_pts,\n                      /*const vector<string>& msg,*/\n                      const string& msg\n                    )\n{\n  // ptsA : ptsB : 2xN or 3xN\n\n  assert( imA.rows == imB.rows && imA.rows > 0  );\n  assert( imA.cols == imB.cols && imA.cols > 0  );\n  assert( ptsA.cols() == ptsB.cols() && ptsA.cols() > 0 );\n  // assert( mask.size() == ptsA.cols() );\n\n  cv::Mat outImg_;\n  cv::hconcat(imA, imB, outImg_);\n\n    cv::Mat outImg;\n    if( outImg_.channels() == 3 )\n        outImg = outImg_;\n    else\n        cv::cvtColor( outImg_, outImg, CV_GRAY2BGR );\n\n\n\n\n  // loop over all points\n  int count = 0;\n  for( int kl=0 ; kl<ptsA.cols() ; kl++ )\n  {\n    // if( mask(kl) == 0 )\n    //   continue;\n\n    count++;\n    cv::Point2d A( ptsA(0,kl), ptsA(1,kl) );\n    cv::Point2d B( ptsB(0,kl), ptsB(1,kl) );\n\n    cv::circle( outImg, A, 2,color_marker, -1 );\n    cv::circle( outImg, B+cv::Point2d(imA.cols,0), 2,color_marker, -1 );\n\n    cv::line( outImg,  A, B+cv::Point2d(imA.cols,0), color_line );\n\n    if( annotate_pts )\n    {\n      cv::putText( outImg, to_string(kl), A, cv::FONT_HERSHEY_SIMPLEX, 0.3, color_marker, 1 );\n      cv::putText( outImg, to_string(kl), B+cv::Point2d(imA.cols,0), cv::FONT_HERSHEY_SIMPLEX, 0.3, color_marker, 1 );\n    }\n  }\n\n\n\n  cv::Mat status = cv::Mat(150, outImg.cols, CV_8UC3, cv::Scalar(0,0,0) );\n  cv::putText( status, to_string(idxA).c_str(), cv::Point(10,30), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(255,255,255), 2 );\n  cv::putText( status, to_string(idxB).c_str(), cv::Point(imA.cols+10,30), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(255,255,255), 2 );\n  cv::putText( status, \"marked # pts: \"+to_string(count), cv::Point(10,60), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(255,255,255), 1.5 );\n\n\n  // put msg in status image\n  if( msg.length() > 0 ) { // ':' separated. Each will go in new line\n      std::vector<std::string> msg_tokens = split(msg, ';');\n      for( int h=0 ; h<msg_tokens.size() ; h++ )\n          cv::putText( status, msg_tokens[h].c_str(), cv::Point(10,80+20*h), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(255,255,255), 1.5 );\n  }\n\n\n  cv::vconcat( outImg, status, dst );\n\n}\n\ncv::Scalar MiscUtils::getFalseColor( float f )\n{\n    cv::Mat colormap_gray = cv::Mat::zeros( 1, 256, CV_8UC1 );\n    cv::Mat colormap_color;\n    for( int i=0 ; i<256; i++ ) colormap_gray.at<uchar>(0,i) = i;\n    cv::applyColorMap(colormap_gray, colormap_color, cv::COLORMAP_JET\t);\n    if( f<0 ) {\n        cv::Vec3b f_ = colormap_color.at<cv::Vec3b>(0,  (int)0 );\n        cv::Scalar color_marker = cv::Scalar(f_[0],f_[1],f_[2]);\n        return color_marker;\n    }\n    if( f>1. ) {\n        cv::Vec3b f_ = colormap_color.at<cv::Vec3b>(0,  (int)255 );\n        cv::Scalar color_marker = cv::Scalar(f_[0],f_[1],f_[2]);\n        return color_marker;\n    }\n\n    int idx = (int) (f*255.);\n    cv::Vec3b f_ = colormap_color.at<cv::Vec3b>(0,  (int)idx );\n    cv::Scalar color_marker = cv::Scalar(f_[0],f_[1],f_[2]);\n    return color_marker;\n}\n\nvoid MiscUtils::plot_point_pair( const cv::Mat& imA, const MatrixXd& ptsA, int idxA,\n                      const cv::Mat& imB, const MatrixXd& ptsB, int idxB,\n                      cv::Mat& dst,\n                      short color_map_direction,\n                      const string& msg\n                     )\n{\n  // ptsA : ptsB : 2xN or 3xN\n  assert( color_map_direction >= 0 && color_map_direction<=3 );\n  assert( imA.rows == imB.rows && imA.rows > 0  );\n  assert( imA.cols == imB.cols && imA.cols > 0  );\n  assert( ptsA.cols() == ptsB.cols() && ptsA.cols() > 0 );\n  // assert( mask.size() == ptsA.cols() );\n\n\n  // make colormap\n  cv::Mat colormap_gray = cv::Mat::zeros( 1, 256, CV_8UC1 );\n  for( int i=0 ; i<256; i++ ) colormap_gray.at<uchar>(0,i) = i;\n  cv::Mat colormap_color;\n  cv::applyColorMap(colormap_gray, colormap_color, cv::COLORMAP_HSV);\n\n\n\n  cv::Mat outImg_;\n  cv::hconcat(imA, imB, outImg_);\n\n  cv::Mat outImg;\n  if( outImg_.channels() == 3 )\n      outImg = outImg_;\n  else\n      cv::cvtColor( outImg_, outImg, CV_GRAY2BGR );\n\n\n\n\n\n  // loop over all points\n  int count = 0;\n  for( int kl=0 ; kl<ptsA.cols() ; kl++ )\n  {\n    // if( mask(kl) == 0 )\n    //   continue;\n\n    count++;\n    cv::Point2d A( ptsA(0,kl), ptsA(1,kl) );\n    cv::Point2d B( ptsB(0,kl), ptsB(1,kl) );\n\n    int coloridx;\n    if( color_map_direction==0 ) coloridx = (int) (ptsA(0,kl)/imA.cols*256.); // horizontal-gradiant\n    if( color_map_direction==1 ) coloridx = (int) (ptsA(1,kl)/imA.rows*256.); // vertical-gradiant\n    if( color_map_direction==2 ) coloridx = (int) (   ( ptsA(0,kl) + ptsA(1,kl) ) / (imA.rows + imA.cols )*256.  ); // manhattan-gradiant\n    if( color_map_direction==3 ) coloridx = (int) (   abs( ptsA(0,kl) - imA.rows/2. + ptsA(1,kl) - imA.cols/2. ) / (imA.rows/2. + imA.cols/2. )*256.  ); // image centered manhattan-gradiant\n    if( coloridx<0 || coloridx>255 ) coloridx=0;\n    cv::Vec3b f = colormap_color.at<cv::Vec3b>(0,  (int)coloridx );\n    cv::Scalar color_marker = cv::Scalar(f[0],f[1],f[2]);\n\n    cv::circle( outImg, A, 2,color_marker, -1 );\n    cv::circle( outImg, B+cv::Point2d(imA.cols,0), 2,color_marker, -1 );\n\n    /*\n    cv::line( outImg,  A, B+cv::Point2d(imA.cols,0), color_line );\n\n    if( annotate_pts )\n    {\n      cv::putText( outImg, to_string(kl), A, cv::FONT_HERSHEY_SIMPLEX, 0.3, color_marker, 1 );\n      cv::putText( outImg, to_string(kl), B+cv::Point2d(imA.cols,0), cv::FONT_HERSHEY_SIMPLEX, 0.3, color_marker, 1 );\n    }\n    */\n  }\n\n\n\n  cv::Mat status = cv::Mat(150, outImg.cols, CV_8UC3, cv::Scalar(0,0,0) );\n  cv::putText( status, to_string(idxA).c_str(), cv::Point(10,30), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(255,255,255), 2 );\n  cv::putText( status, to_string(idxB).c_str(), cv::Point(imA.cols+10,30), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(255,255,255), 2 );\n  cv::putText( status, \"marked # pts: \"+to_string(count), cv::Point(10,60), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(255,255,255), 1.5 );\n\n\n  // put msg in status image\n  if( msg.length() > 0 ) { // ':' separated. Each will go in new line\n      std::vector<std::string> msg_tokens = split(msg, ';');\n      for( int h=0 ; h<msg_tokens.size() ; h++ )\n          cv::putText( status, msg_tokens[h].c_str(), cv::Point(10,80+20*h), cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(255,255,255), 1.5 );\n  }\n\n\n  cv::vconcat( outImg, status, dst );\n\n}\n\n\n// append a status image . ';' separated\nvoid MiscUtils::append_status_image( cv::Mat& im, const string& msg, float txt_size, cv::Scalar bg_color, cv::Scalar txt_color, float line_thinkness )\n{\n    bool is_single_channel = (im.channels()==1)?true:false;\n    txt_size = (txt_size<0.1 || txt_size>2)?0.4:txt_size;\n\n    std::vector<std::string> msg_tokens = split(msg, ';');\n    int status_im_height = 50+20*msg_tokens.size();\n\n    cv::Mat status;\n    if( is_single_channel )\n        status = cv::Mat(status_im_height, im.cols, CV_8UC1, cv::Scalar(0,0,0) );\n    else\n        status = cv::Mat(status_im_height, im.cols, CV_8UC3, bg_color );\n\n\n    for( int h=0 ; h<msg_tokens.size() ; h++ )\n        cv::putText( status, msg_tokens[h].c_str(), cv::Point(10,20+20*h),\n                cv::FONT_HERSHEY_SIMPLEX,\n                txt_size, txt_color, line_thinkness );\n\n\n    cv::vconcat( im, status, im );\n\n\n}\n\nbool MiscUtils::side_by_side( const cv::Mat& A, const cv::Mat& B, cv::Mat& dst )\n{\n    if( A.rows == B.rows && A.channels() == B.channels() ) {\n        cv::hconcat(A, B, dst);\n        return true;\n    }\n    else {\n        dst = cv::Mat();\n        return false;\n    }\n}\n\nbool MiscUtils::vertical_side_by_side( const cv::Mat& A, const cv::Mat& B, cv::Mat& dst )\n{\n    if( A.cols == B.cols && A.channels() == B.channels() ) {\n        cv::vconcat(A, B, dst);\n        return true;\n    }\n    else {\n        dst = cv::Mat();\n        return false;\n    }\n}\n\ndouble MiscUtils::Slope(int x0, int y0, int x1, int y1){\n     return (double)(y1-y0)/(x1-x0);\n}\n\nvoid MiscUtils::draw_fullLine(cv::Mat& img, cv::Point2f a, cv::Point2f b, cv::Scalar color){\n     double slope = MiscUtils::Slope(a.x, a.y, b.x, b.y);\n\n     cv::Point2f p(0,0), q(img.cols,img.rows);\n\n     p.y = -(a.x - p.x) * slope + a.y;\n     q.y = -(b.x - q.x) * slope + b.y;\n\n     cv::line(img,p,q,color,1,8,0);\n}\n\n// draw line on the image, given a line equation in homogeneous co-ordinates. l = (a,b,c) for ax+by+c = 0\nvoid MiscUtils::draw_line( const Vector3d l, cv::Mat& im, cv::Scalar color )\n{\n    // C++: void line(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0)\n    if( l(0) == 0 ) {\n        // plot y = -c/b\n        cv::Point2f a(0.0, -l(2)/l(1) );\n        cv::Point2f a_(10.0, -l(2)/l(1) );\n        MiscUtils::draw_fullLine( im, a, a_, color );\n        return;\n    }\n\n    if( l(1) == 0 ) {\n        // plot x = -c/a\n        cv::Point2f b(-l(2)/l(0), 0.0 );\n        cv::Point2f b_(-l(2)/l(0), 10.0 );\n        MiscUtils::draw_fullLine( im, b, b_, color );\n        return;\n    }\n\n    cv::Point2f a(0.0, -l(2)/l(1) );\n    cv::Point2f b(-l(2)/l(0), 0.0 );\n    // cout << a << \"<--->\" << b << endl;\n    // cv::line( im, a, b, cv::Scalar(255,255,255) );\n    MiscUtils::draw_fullLine( im, a, b, color );\n}\n\n// mark point on the image, pt is in homogeneous co-ordinate.\nvoid MiscUtils::draw_point( const Vector3d pt, cv::Mat& im, cv::Scalar color  )\n{\n    // C++: void circle(Mat& img, Point center, int radius, const Scalar& color, int thickness=1, int lineType=8, int shift=0)\n    cv::circle( im, cv::Point2f( pt(0)/pt(2), pt(1)/pt(2) ), 2, color, -1   );\n\n}\n\n// mark point on image\nvoid MiscUtils::draw_point( const Vector2d pt, cv::Mat& im, cv::Scalar color  )\n{\n    cv::circle( im, cv::Point2f( pt(0), pt(1) ), 2, color, -1   );\n}\n"
  },
  {
    "path": "src/utils/MiscUtils.h",
    "content": "#pragma once\n\n#include <iostream>\n#include <string>\n#include <vector>\n#include <fstream>\n#include <queue>\n#include <ostream>\n\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n#include <Eigen/Core>\n#include <Eigen/Dense>\n#include <Eigen/Geometry>\nusing namespace Eigen;\n#include <opencv2/core/eigen.hpp>\n\nusing namespace std;\n\n#include \"GMSMatcher/gms_matcher.h\"\n#include \"ElapsedTime.h\"\n\n#include <sensor_msgs/Image.h>\n#include <cv_bridge/cv_bridge.h>\n\n\nclass MiscUtils\n{\npublic:\n    static string type2str(int type);\n    static string cvmat_info( const cv::Mat& mat );\n    static string imgmsg_info(const sensor_msgs::ImageConstPtr &img_msg);\n    static string imgmsg_info(const sensor_msgs::Image& img_msg);\n    static cv::Mat getImageFromMsg(const sensor_msgs::ImageConstPtr &img_msg);\n\n    static std::vector<std::string>\n    split( std::string const& original, char separator );\n\n\n    //---------------------------- Conversions ---------------------------------//\n    // convert from opencv format of keypoints to Eigen\n    static void keypoint_2_eigen( const std::vector<cv::KeyPoint>& kp, MatrixXd& uv, bool make_homogeneous=true );\n\n    // given opencv keypoints and DMatch will produce M1, and M2 the co-ordinates\n    static void dmatch_2_eigen( const std::vector<cv::KeyPoint>& kp1, const std::vector<cv::KeyPoint>& kp2,\n                                const std::vector<cv::DMatch> matches,\n                                MatrixXd& M1, MatrixXd& M2,\n                                bool make_homogeneous=true\n                            );\n\n\n    //---------------------------- Conversions ---------------------------------//\n\n\n\n    //--------------------- Plot Keypoints on Image ----------------------------//\n    // Eigen Interace:  PLotting functions with Eigen Interfaces\n    static void plot_point_sets( const cv::Mat& im, const MatrixXd& pts_set, cv::Mat& dst,\n                                            const cv::Scalar& color, bool enable_keypoint_annotation=true,const string& msg=string(\"\") );\n\n    // cv::Mat Interfaces: Plotting Functions with cv::Mat Interfaces.\n    static void plot_point_sets( const cv::Mat& im, const cv::Mat& pts_set, cv::Mat& dst,\n                                            const cv::Scalar& color, bool enable_keypoint_annotation=true, const string& msg=string(\"\") );\n\n    // Inplace plotting. Here dont need to specify a separate destination. src is modified.\n    static void plot_point_sets( cv::Mat& im, const MatrixXd& pts_set,\n                                            const cv::Scalar& color, bool enable_keypoint_annotation=true, const string& msg=string(\"\") );\n\n    // Plotting with annotations specified by VectorXi\n    static void plot_point_sets( cv::Mat& im, const MatrixXd& pts_set, cv::Mat& dst,\n                                            const cv::Scalar& color, const VectorXi& annotations, const string& msg );\n\n    // Plotting with annotations specified by VectorXi inplace\n    static void plot_point_sets( cv::Mat& im, const MatrixXd& pts_set,\n                                            const cv::Scalar& color, const VectorXi& annotations, const string& msg );\n\n    // plot point with colors specified at every point. pts_set : 3xN or 2xN, len(color_annotations) == pts_set.cols()\n    static void plot_point_sets( const cv::Mat& im, const MatrixXd& pts_set, cv::Mat& dst,\n                                            vector<cv::Scalar>& color_annotations, float alpha=0.8, const string& msg=string(\"N/A\") );\n\n    // END--------------------- Plot Keypoints on Image ----------------------------//\n\n\n\n\n    //------------------------------- Plot Matchings on image pair -------------------------//\n\n    // Plots [ imA | imaB ] with points correspondences\n    // [Input]\n    //    imA, imB : Images\n    //    ptsA, ptsB : 2xN or 3xN\n    //    idxA, idxB : Index of each of the image. This will appear in status part. No other imppact of these.\n    //    color_marker : color of the point marker\n    //    color_line   : color of the line\n    //    annotate_pts : true with putText for each point. False will not putText.\n    // [Output]\n    //    outImg : Output image\n    static void plot_point_pair( const cv::Mat& imA, const MatrixXd& ptsA, int idxA,\n                          const cv::Mat& imB, const MatrixXd& ptsB, int idxB,\n                          cv::Mat& dst,\n                          const cv::Scalar& color_marker,\n                          const cv::Scalar& color_line=cv::Scalar(0,255,0),\n                          bool annotate_pts=false,\n                          const string& msg=string(\"N.A\")\n                         );\n\n\n     // nearly same as the above, but will color every co-ordinate with different color\n     // color_map_direction : 0 ==> // horizontal-gradiant\n     //                       1 ==>  // vertical-gradiant\n     //                       2 ==> // manhattan-gradiant\n     //                       3 ==> // image centered manhattan-gradiant\n     static void plot_point_pair( const cv::Mat& imA, const MatrixXd& ptsA, int idxA,\n                           const cv::Mat& imB, const MatrixXd& ptsB, int idxB,\n                           cv::Mat& dst,\n                           short color_map_direction,\n                           const string& msg=string(\"N.A\")\n                          );\n\n    //------------------------------- Plot Matchings on image pair -------------------------//\n\n\n    //------------------------- Points and Lines on Images --------------------------------//\n\n    // Given two image-points draw line between them, extend both ways. Infinite line-segments\n    static void draw_fullLine(cv::Mat& img, cv::Point2f a, cv::Point2f b, cv::Scalar color);\n\n    // draw line on the image, given a line equation in homogeneous co-ordinates. l = (a,b,c) for ax+by+c = 0\n    static void draw_line( const Vector3d l, cv::Mat& im, cv::Scalar color );\n\n    // mark point on the image, pt is in homogeneous co-ordinate.\n    static void draw_point( const Vector3d pt, cv::Mat& im, cv::Scalar color  );\n\n    // mark point on image\n    static void draw_point( const Vector2d pt, cv::Mat& im, cv::Scalar color  );\n\n\n    // append a status image . ';' separated\n    static void append_status_image( cv::Mat& im, const string& msg, float txt_size=0.4, cv::Scalar bg_color=cv::Scalar(0,0,0), cv::Scalar txt_color=cv::Scalar(255,255,255), float line_thinkness=1.5 );\n    static bool side_by_side( const cv::Mat& A, const cv::Mat& B, cv::Mat& dst );\n    static bool vertical_side_by_side( const cv::Mat& A, const cv::Mat& B, cv::Mat& dst );\n\n    // END ------------------------- Points and Lines on Images --------------------------------//\n\n\n    // [Input] : f a float between 0 and 1.\n    // [Output]: cv::Scalar gives out a bgr color pallet.\n    // Note: This is inefficient, don't use it too often. If you are going to do lot of quering for colors use `class FalseColors`\n    static cv::Scalar getFalseColor( float f );\n\n\nprivate:\n\n    static double Slope(int x0, int y0, int x1, int y1);\n\n};\n\n\nclass FalseColors\n{\npublic:\n    FalseColors() {\n        cv::Mat colormap_gray = cv::Mat::zeros( 1, 256, CV_8UC1 );\n        for( int i=0 ; i<256; i++ ) colormap_gray.at<uchar>(0,i) = i;\n        cv::applyColorMap(colormap_gray, colormap_color, cv::COLORMAP_JET\t);\n    }\n\n    cv::Scalar getFalseColor( float f ) {\n        int idx = (int) (f*255.);\n        if( f<0 ) {\n            idx=0;\n        }\n        if( f>255 ) {\n            idx=255;\n        }\n\n\n        cv::Vec3b f_ = colormap_color.at<cv::Vec3b>(0,  (int)idx );\n        cv::Scalar color_marker = cv::Scalar(f_[0],f_[1],f_[2]);\n        return color_marker;\n    }\n\n    cv::Mat getStrip( int nrows, int ncols ) {\n        cv::Mat colormap_gray = cv::Mat::zeros( nrows, ncols, CV_8UC1 );\n\n        for( int r=0; r<nrows; r++ ) {\n            for( int c=0 ; c<ncols; c++ )\n                colormap_gray.at<uchar>(r,c) = (uchar) ( (float(c)/ncols)*256 );\n        }\n\n        cv::Mat __dst;\n        cv::applyColorMap(colormap_gray, __dst, cv::COLORMAP_JET\t);\n        return __dst;\n    }\n\n\nprivate:\n    cv::Mat colormap_color;\n\n\n};\n"
  },
  {
    "path": "src/utils/Plot2Mat.cpp",
    "content": "#include \"Plot2Mat.h\"\n\nPlot2Mat::Plot2Mat() {\n    im_width = 640;\n    im_height = 480;\n    bg_color = cv::Scalar(80,80,80);\n    create( bg_color );\n}\n\nPlot2Mat::Plot2Mat(int _width, int _height, cv::Scalar _bg_color)\n{\n    im_width = _width;\n    im_height = _height;\n    bg_color = _bg_color;\n    create( bg_color );\n}\n\n\nvoid Plot2Mat::resetCanvas() {\n    create( bg_color );\n}\n\nvoid Plot2Mat::setYminmax( float _ymin, float _ymax ) {\n    assert( _ymin<_ymax );\n    ymin=_ymin;\n    ymax=_ymax;\n    setYminmax_dynamically = false;\n}\n\nvoid Plot2Mat::setYminmaxDynamic() {\n    setYminmax_dynamically = true;\n}\n\nconst cv::Mat& Plot2Mat::getCanvasConstPtr()\n{\n    return (const cv::Mat&) im;\n}\n\n\nvoid Plot2Mat::create( const cv::Scalar bg_color )\n{\n    this->im = cv::Mat( int(im_height*1.2), int(im_width*1.2), CV_8UC3, bg_color );\n}\n\n\n\nbool Plot2Mat::plot( const VectorXd& y, const cv::Scalar line_color, bool mark_plottedpts, bool label_plottedpts )\n{\n    // cout << \"y.rows = \" << y.rows() << endl;\n    L = y.rows();\n    assert( y.rows() > 0 );\n\n\n    auto x = VectorXd( L );\n    for( int i=0; i<L ; i++ ) x(i) = i;\n\n    auto xd = x / L * im_width;\n\n\n    if( setYminmax_dynamically ) {\n    ymin = y.minCoeff(); //0.0;\n    ymax = y.maxCoeff(); //1.0;\n    }\n\n    VectorXd yd = (y - ymin* VectorXd::Ones(L)) / (ymax-ymin) * im_height;\n\n    // plot each point as circle\n    for( int i=0 ; i<L ; i++ ) {\n        // cout << \"draw circle at \" << xd(i) << \",\" << yd(i) << endl;\n        if( mark_plottedpts || label_plottedpts ) {\n            auto c = cv::Point2f( xd(i), -yd(i)+im_height );\n            // plot the pt as circle\n            if( mark_plottedpts )\n                cv::circle( im, c, 6, cv::Scalar(0,0,255), -1 );\n\n            // mark the text.\n            if( label_plottedpts ) {\n                char cordinate_pt_str[50];\n                snprintf( cordinate_pt_str, 50, \"%4.2f,%4.2f\", x(i), y(i) );\n                cv::putText(im, cordinate_pt_str, c, cv::FONT_HERSHEY_COMPLEX_SMALL, 0.8, cv::Scalar(0,50,200), 1, CV_AA);\n            }\n        }\n\n        auto c_xlabel = cv::Point2f( xd(i), im_height );\n        if( i%int(L/5) == 0 )\n        {\n            cv::putText(im, std::to_string(i), cv::Point2f(c_xlabel.x,c_xlabel.y+20), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.8, cv::Scalar(255,255,255), 1, CV_AA);\n\n            cv::circle( im, c_xlabel, 4, cv::Scalar(255,255,255), -1 );\n            cv::line( im, c_xlabel, cv::Point2f(c_xlabel.x, 0), cv::Scalar(255,255,255), 2 );\n        }\n    }\n\n\n    // lines between pt[i-1] <----> pt[i]\n    for( int i=1 ; i<L ; i++ ) {\n        auto p1 = cv::Point2f( xd(i), -yd(i) + im_height );\n        auto p2 = cv::Point2f( xd(i-1), -yd(i-1) + im_height );\n        cv::line( im, p1, p2, line_color, 2 );\n    }\n\n\n    // ylabels\n    double step = (ymax - ymin)/10.;\n    for( double _yd = ymin ; _yd<=ymax ; _yd+=step )\n    {\n        double _y = (_yd - ymin) / (ymax-ymin) * im_height;\n        _y = -_y + im_height;\n        cv::line( im, cv::Point2f(0., _y), cv::Point2f(im_width, _y), cv::Scalar(255,255,255), 2 );\n\n        char buf[15];\n        snprintf( buf, 15, \"%.2f\", _yd );\n        cv::putText(im, buf, cv::Point2f(im_width+20,_y), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.8, cv::Scalar(255,255,255), 1, CV_AA);\n    }\n}\n\n\n\n\nbool Plot2Mat::mark( const int x_, const cv::Scalar color, bool enable_text_label )\n{\n    // cout << \"Plot2Mat::mark(\" << x_ << \")\\n\";\n    assert( L>0 && \"you called mark before plotting. the intended use is to call plot and then optionally call mark\" );\n    float xd = float(x_) / float(L) * im_width;\n    auto c = cv::Point2f( xd, im_height );\n    cv::circle( im, c, 7, color, -1 );\n\n    if( enable_text_label )\n        cv::putText(im, to_string(x_), c, cv::FONT_HERSHEY_COMPLEX_SMALL, 0.8, color, 1, CV_AA);\n\n}\n"
  },
  {
    "path": "src/utils/Plot2Mat.h",
    "content": "#pragma once\n/**\nA OpenCV based plotting. The idea is, given a vector (of floats) to\nplot them and make a cv::Mat out of it.\n\nAuthor  : Manohar Kuse <mpkuse@connect.ust.hk>\nCreated : 9th Apr, 2019\n**/\n\n\n/****\n\nimport cv2\nimport numpy as np\n\nimport matplotlib\nimport code\n\nclass Plot2Mat:\n    def __init__(self):\n        self.width = 640\n        self.height = 480\n        self.im = None\n        self.create()\n\n    def xprint( self, msg  ):\n        print '[Plot2Mat]', msg\n\n    def create( self ):\n        self.im = np.zeros( ( int(self.height*1.2), int(self.width*1.2), 3) ) + 80\n\n    def mark( self, xm_array, color=(0,0,255) ):\n        \"\"\" Give 1 iteration number (x-axis) to mark. This can be called after plot \"\"\"\n        # self.xprint( 'mark called'+str(xm_array.shape) )\n        if self.im is None:\n            return self.im\n        # self.xprint( str(xm_array.shape) )\n        for xm in xm_array:\n            # self.xprint( str(xm) )\n            xd = xm / self.L * self.width\n            c = (  int(xd), int(self.height)  )\n            self.im = cv2.circle( self.im, c, 7, color, -1  )\n\n        cv2.putText(self.im, 'loop_items = %d' %(len(xm_array)), ( 10,40 ), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255,255,255), 3)\n        return self.im\n\n\n\n    def plot( self, y, line_color=(0,255,0) ):\n        \"\"\" y is a 1-d array. Plot with indices. \"\"\"\n        im = self.im\n\n\n        x = np.array( range( len(y) ) ) # [0 to N] ---> [ 0 to self.width ]\n        L = float( len(x) )\n        self.L = L\n\n        # y [ min(y) to max(y) ] ---> [ 0 to self.height ]\n\n        # code.interact( local=locals() )\n        xd = x / ( L ) * (self.width)\n\n        ymin = 0.0#y.min()\n        ymax = 1.0#y.max()\n        yd = (y - ymin) / (ymax - ymin) * (self.height)\n\n        # Plot each point as circle\n        for i in range( int(L) ):\n            # print 'draw circle at ', xd[i], yd[i]\n            c = (  int(xd[i]), int(-yd[i] + self.height)  ) # circle center\n            # cv2.circle(im, c, 2, (0,0,255) )\n\n            c_xlabel = (  int(xd[i]), int(0.0 + self.height)  )\n            if i % int(L/5) == 0 :\n                cv2.circle(im, c_xlabel, 4, (255,255,255), -1 )\n                cv2.putText(im,str(i), (c_xlabel[0],c_xlabel[1]+50), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (255,255,255), 3)\n\n                cv2.line( im, c_xlabel, (c_xlabel[0],0), (255,255,255), 2 )\n\n\n        for i in range( 1, int(L) ):\n            p1 = (  int(xd[i]), int(-yd[i] + self.height)  ) # circle center\n            p2 = (  int(xd[i-1]), int(-yd[i-1] + self.height)  ) # circle center\n            cv2.line( im, p1, p2, line_color, 3 )\n\n            # cv2.line(  )\n\n\n        for _yd in np.linspace( 0, 1, 11 ):\n            #line (in image cord space): (0, _y) --- (self.width, _y)\n            _y = (_yd) / (ymax - ymin) * self.height   + ymin\n            _y = int(-_y+self.height)\n            # print '(%d,%d) <---> (%d,%d)' %(0,_y, self.width, _y)\n            cv2.line( im, (0,_y), (self.width, _y), (255,255,255),3)\n\n            cv2.putText(im,str(_yd),(self.width+40, _y+10), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255,255,255), 3)\n\n        self.im = im\n        return im\n\n****/\n\n#include <iostream>\n#include <string>\n#include <vector>\n#include <fstream>\n#include <queue>\n#include <ostream>\n\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n#include <Eigen/Core>\n#include <Eigen/Dense>\n#include <Eigen/Geometry>\nusing namespace Eigen;\n#include <opencv2/core/eigen.hpp>\n\nusing namespace std;\n\n\nclass Plot2Mat {\n\npublic:\n    Plot2Mat();\n    Plot2Mat(int _width, int _height, cv::Scalar _bg_color);\n\n    // Canvas controllers\n    void resetCanvas();\n\n    void setYminmax( float _ymin, float _ymax );\n    void setYminmaxDynamic();\n\n\n\n    bool mark( const int x_, const cv::Scalar color=cv::Scalar(0,0,255), bool enable_text_label=true );\n\n\n    //  y is a 1-d array. Plot with indices.\n    // mark_plottedpts : This will draw circles for each pt plotted.\n    // label_plottedpts : This will write in text (using cv::putText) the co-ordinate\n    bool plot( const VectorXd& y, const cv::Scalar line_color=cv::Scalar(0,255,0), bool mark_plottedpts=false, bool label_plottedpts=false  );\n\n    const cv::Mat& getCanvasConstPtr();\n\nprivate:\n    void create(const cv::Scalar bg_color=cv::Scalar(80,80,80));\n    int im_width;\n    int im_height;\n    cv::Mat im;\n    cv::Scalar bg_color = cv::Scalar(80,80,80);\n\n    int L=-1;//length of y-plot\n\n    double ymin, ymax;\n    bool setYminmax_dynamically=true;\n};\n"
  },
  {
    "path": "src/utils/PointFeatureMatching.cpp",
    "content": "#include \"PointFeatureMatching.h\"\n\n// #define ___StaticPointFeatureMatching__gms_point_feature_matches( msg ) msg;\n#define ___StaticPointFeatureMatching__gms_point_feature_matches( msg ) ;\nvoid StaticPointFeatureMatching::gms_point_feature_matches( const cv::Mat& imleft_undistorted, const cv::Mat& imright_undistorted,\n                            MatrixXd& u, MatrixXd& ud, int n_orb_feat )\n{\n    ElapsedTime timer;\n\n\n    //\n    // Point feature and descriptors extract\n    std::vector<cv::KeyPoint> kp1, kp2;\n    cv::Mat d1, d2; //< descriptors\n\n    // cv::Ptr<cv::ORB> orb = cv::ORB::create(5000);\n    cv::Ptr<cv::ORB> orb = cv::ORB::create(n_orb_feat);\n    orb->setFastThreshold(0);\n\n    ___StaticPointFeatureMatching__gms_point_feature_matches(timer.tic();)\n    orb->detectAndCompute(imleft_undistorted, cv::Mat(), kp1, d1);\n    orb->detectAndCompute(imright_undistorted, cv::Mat(), kp2, d2);\n    ___StaticPointFeatureMatching__gms_point_feature_matches(cout <<  timer.toc_milli()  << \" (ms) 2X detectAndCompute(ms) : \"<< endl;)\n    // std::cout << \"d1 \" << MiscUtils::cvmat_info( d1 ) << std::endl;\n    // std::cout << \"d2 \" << MiscUtils::cvmat_info( d2 ) << std::endl;\n\n    //plot\n    // cv::Mat dst_left, dst_right;\n    // MatrixXd e_kp1, e_kp2;\n    // MiscUtils::keypoint_2_eigen( kp1, e_kp1 );\n    // MiscUtils::keypoint_2_eigen( kp2, e_kp2 );\n    // MiscUtils::plot_point_sets( imleft_undistorted, e_kp1, dst_left, cv::Scalar(0,0,255), false );\n    // MiscUtils::plot_point_sets( imright_undistorted, e_kp2, dst_right, cv::Scalar(0,0,255), false );\n    // cv::imshow( \"dst_left\", dst_left );\n    // cv::imshow( \"dst_right\", dst_right );\n\n    //\n    // Point feature matching\n    cv::BFMatcher matcher(cv::NORM_HAMMING); // TODO try FLANN matcher here.\n    vector<cv::DMatch> matches_all, matches_gms;\n    ___StaticPointFeatureMatching__gms_point_feature_matches(timer.tic();)\n    matcher.match(d1, d2, matches_all);\n    ___StaticPointFeatureMatching__gms_point_feature_matches(\n    std::cout << timer.toc_milli() << \" : (ms) BFMatcher took (ms)\\t\";\n    std::cout << \"BFMatcher : npts = \" << matches_all.size() << std::endl;\n    )\n\n\n    // gms_matcher\n    ___StaticPointFeatureMatching__gms_point_feature_matches(timer.tic();)\n    std::vector<bool> vbInliers;\n    gms_matcher gms(kp1, imleft_undistorted.size(), kp2, imright_undistorted.size(), matches_all);\n    int num_inliers = gms.GetInlierMask(vbInliers, false, false);\n    ___StaticPointFeatureMatching__gms_point_feature_matches(\n    cout << timer.toc_milli() << \" : (ms) GMSMatcher took.\\t\" ;\n    cout << \"Got total gms matches \" << num_inliers << \" matches.\" << endl;\n    )\n\n    // collect matches\n    for (size_t i = 0; i < vbInliers.size(); ++i)\n    {\n        if (vbInliers[i] == true)\n        {\n            matches_gms.push_back(matches_all[i]);\n        }\n    }\n    // MatrixXd M1, M2;\n    if( matches_gms.size() > 0 )\n        MiscUtils::dmatch_2_eigen( kp1, kp2, matches_gms, u, ud, true );\n\n\n}\n\n\n\n\n//-----------------------------------------------\n//-------- NEW ----------------------------------\n//-----------------------------------------------\n// Given the point feature matches and the 3d image (from disparity map) will return\n// the valid world points and corresponding points.\n// [Input]\n//      uv: 2xN matrix of point-feature in image-a. In image co-ordinates (not normalized image cords)\n//      _3dImage_uv : 3d image from disparity map of image-a. sizeof( _3dImage_uv) === WxHx3\n//      uv_d: 2xN matrix of point-feature in image-b. Note that uv<-->uv_d are correspondences so should of equal sizes\n// [Output]\n//      feature_position_uv : a subset of uv but normalized_image_cordinates\n//      feature_position_uv_d : a subset of uv_d. results in normalized_image_cordinates\n//      world_point : 3d points of uv.\n// [Note]\n//      feature_position_uv \\subset uv. Selects points which have valid depths.\n//      size of output is same for all 3\n//      world points are of uv and in co-ordinate system of camera center of uv (or image-a).\n\nbool StaticPointFeatureMatching::make_3d_2d_collection__using__pfmatches_and_disparity( std::shared_ptr<StereoGeometry> stereogeom,\n            const MatrixXd& uv, const cv::Mat& _3dImage_uv,     const MatrixXd& uv_d,\n                            std::vector<Eigen::Vector2d>& feature_position_uv, std::vector<Eigen::Vector2d>& feature_position_uv_d,\n                            std::vector<Eigen::Vector3d>& world_point )\n{\n    assert( (uv.cols() == uv_d.cols() ) && \"[StaticPointFeatureMatching::make_3d_2d_collection__using__pfmatches_and_disparity] pf-matches need to be of same length. You provided of different lengths\\n\" );\n    assert( _3dImage_uv.type() == CV_32FC3 );\n\n    if( uv.cols() != uv_d.cols() ) {\n        cout << TermColor::RED() << \"[StaticPointFeatureMatching::make_3d_2d_collection__using__pfmatches_and_disparity] pf-matches need to be of same length. You provided of different lengths\\n\" << TermColor::RESET();\n        return false;\n    }\n\n    if( _3dImage_uv.type() != CV_32FC3 && _3dImage_uv.rows <= 0 && _3dImage_uv.cols <= 0  ) {\n        cout << TermColor::RED() << \"[StaticPointFeatureMatching::make_3d_2d_collection__using__pfmatches_and_disparity] _3dImage is expected to be of size CV_32FC3\\n\" << TermColor::RESET();\n        return false;\n    }\n\n\n\n    int c = 0;\n    MatrixXd ud_normalized = stereogeom->get_K().inverse() * uv_d;\n    MatrixXd u_normalized = stereogeom->get_K().inverse() * uv;\n    feature_position_uv.clear();\n    feature_position_uv_d.clear();\n    world_point.clear();\n\n    for( int k=0 ; k<uv.cols() ; k++ )\n    {\n        cv::Vec3f _3dpt = _3dImage_uv.at<cv::Vec3f>( (int)uv(1,k), (int)uv(0,k) );\n        if( _3dpt[2] < 0.1 || _3dpt[2] > 25.  )\n            continue;\n\n        c++;\n        #if 0\n        cout << TermColor::RED() << \"---\" << k << \"---\" << TermColor::RESET() << endl;\n        cout << \"ud=\" << ud.col(k).transpose() ;\n        cout << \" <--> \";\n        cout << \"u=\" << u.col(k).transpose() ;\n        cout << \"  3dpt of u=\";\n        cout <<  TermColor::GREEN() << _3dpt[0] << \" \" << _3dpt[1] << \" \" << _3dpt[2] << \" \" << TermColor::RESET();\n        cout << endl;\n        #endif\n\n        feature_position_uv.push_back( Vector2d( u_normalized(0,k), u_normalized(1,k) ) );\n        feature_position_uv_d.push_back( Vector2d( ud_normalized(0,k), ud_normalized(1,k) ) );\n        world_point.push_back( Vector3d( _3dpt[0], _3dpt[1], _3dpt[2] ) );\n    }\n\n    // cout << \"[make_3d_2d_collection__using__pfmatches_and_disparity]of the total \" << uv.cols() << \" point feature correspondences \" << c << \" had valid depths\\n\";\n\n    return true;\n\n    if( c < 30 ) {\n        cout << TermColor::RED() << \"too few valid 3d points between frames\" <<  TermColor::RESET() << endl;\n        return false;\n    }\n\n}\n\n// given pf-matches uv<-->ud_d and their _3dImages. returns the 3d point correspondences at points where it is valid\n// uv_X: the 3d points are in frame of ref of camera-uv\n// uvd_Y: these 3d points are in frame of ref of camera-uvd\nbool StaticPointFeatureMatching::make_3d_3d_collection__using__pfmatches_and_disparity(\n    const MatrixXd& uv, const cv::Mat& _3dImage_uv,\n    const MatrixXd& uv_d, const cv::Mat& _3dImage_uv_d,\n    vector<Vector3d>& uv_X, vector<Vector3d>& uvd_Y\n)\n{\n    assert( uv.cols() > 0 && uv.cols() == uv_d.cols() );\n    uv_X.clear();\n    uvd_Y.clear();\n    if( uv.cols() != uv_d.cols() ) {\n        cout << TermColor::RED() << \"[StaticPointFeatureMatching::make_3d_3d_collection__using__pfmatches_and_disparity] pf-matches need to be of same length. You provided of different lengths\\n\" << TermColor::RESET();\n        return false;\n    }\n\n    if( _3dImage_uv.type() != CV_32FC3 && _3dImage_uv.rows <= 0 && _3dImage_uv.cols <= 0  ) {\n        cout << TermColor::RED() << \"[StaticPointFeatureMatching::make_3d_3d_collection__using__pfmatches_and_disparity] _3dImage is expected to be of size CV_32FC3\\n\" << TermColor::RESET();\n        return false;\n    }\n\n\n    // similar to above but should return world_point__uv and world_point__uv_d\n    int c=0;\n    for( int k=0 ; k<uv.cols() ; k++ )\n    {\n        cv::Vec3f uv_3dpt = _3dImage_uv.at<cv::Vec3f>( (int)uv(1,k), (int)uv(0,k) );\n        cv::Vec3f uvd_3dpt = _3dImage_uv_d.at<cv::Vec3f>( (int)uv_d(1,k), (int)uv_d(0,k) );\n\n        if( uv_3dpt[2] < 0.1 || uv_3dpt[2] > 25. || uvd_3dpt[2] < 0.1 || uvd_3dpt[2] > 25.  )\n            continue;\n\n        uv_X.push_back( Vector3d( uv_3dpt[0], uv_3dpt[1], uv_3dpt[2] ) );\n        uvd_Y.push_back( Vector3d( uvd_3dpt[0], uvd_3dpt[1], uvd_3dpt[2] ) );\n        c++;\n\n    }\n    // cout << \"[make_3d_3d_collection__using__pfmatches_and_disparity] of the total \" << uv.cols() << \" point feature correspondences \" << c << \" had valid depths\\n\";\n    return true;\n}\n"
  },
  {
    "path": "src/utils/PointFeatureMatching.h",
    "content": "#pragma once\n#include <iostream>\n#include <string>\n#include <vector>\n#include <fstream>\n#include <queue>\n#include <ostream>\n#include <memory> //for std::shared_ptr\n\n\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n#include <Eigen/Core>\n#include <Eigen/Dense>\n#include <Eigen/Geometry>\nusing namespace Eigen;\n#include <opencv2/core/eigen.hpp>\n\nusing namespace std;\n\n#include \"GMSMatcher/gms_matcher.h\"\n#include \"ElapsedTime.h\"\n#include \"MiscUtils.h\"\n#include \"TermColor.h\"\n#include \"CameraGeometry.h\"\n\n\nclass StaticPointFeatureMatching\n{\npublic:\n\n    static void gms_point_feature_matches( const cv::Mat& imleft_undistorted, const cv::Mat& imright_undistorted,\n                                MatrixXd& u, MatrixXd& ud, int n_orb_feat=5000 ); //< n_orb_feat has to be a few thousands atleast for spatial consistency checks.\n\n\n    // Given the point feature matches and the 3d image (from disparity map) will return\n    // the valid world points and corresponding points.\n    // [Input]\n    //      uv: 2xN matrix of point-feature in image-a. In image co-ordinates (not normalized image cords)\n    //      _3dImage_uv : 3d image from disparity map of image-a. sizeof( _3dImage_uv) === WxHx3\n    //      uv_d: 2xN matrix of point-feature in image-b. Note that uv<-->uv_d are correspondences so should of equal sizes\n    // [Output]\n    //      feature_position_uv : a subset of uv but normalized_image_cordinates\n    //      feature_position_uv_d : a subset of uv_d. results in normalized_image_cordinates\n    //      world_point : 3d points of uv.\n    // [Note]\n    //      feature_position_uv \\subset uv. Selects points which have valid depths.\n    //      size of output is same for all 3\n    //      world points are of uv and in co-ordinate system of camera center of uv (or image-a).\n    static bool make_3d_2d_collection__using__pfmatches_and_disparity( std::shared_ptr<StereoGeometry> stereogeom,\n                const MatrixXd& uv, const cv::Mat& _3dImage_uv,     const MatrixXd& uv_d,\n                                std::vector<Eigen::Vector2d>& feature_position_uv, std::vector<Eigen::Vector2d>& feature_position_uv_d,\n                                std::vector<Eigen::Vector3d>& world_point );\n\n\n    // given pf-matches uv<-->ud_d and their _3dImages. returns the 3d point correspondences at points where it is valid\n    // uv_X: the 3d points are in frame of ref of camera-uv\n    // uvd_Y: these 3d points are in frame of ref of camera-uvd\n    static bool make_3d_3d_collection__using__pfmatches_and_disparity(\n        const MatrixXd& uv, const cv::Mat& _3dImage_uv,\n        const MatrixXd& uv_d, const cv::Mat& _3dImage_uv_d,\n        vector<Vector3d>& uv_X, vector<Vector3d>& uvd_Y\n    );\n\n};\n"
  },
  {
    "path": "src/utils/PoseManipUtils.cpp",
    "content": "#include \"PoseManipUtils.h\"\n\nvoid PoseManipUtils::raw_to_eigenmat( const double * quat, const double * t, Matrix4d& dstT )\n{\n  Quaterniond q = Quaterniond( quat[0], quat[1], quat[2], quat[3] );\n\n  dstT = Matrix4d::Zero();\n  dstT.topLeftCorner<3,3>() = q.toRotationMatrix();\n\n  dstT(0,3) = t[0];\n  dstT(1,3) = t[1];\n  dstT(2,3) = t[2];\n  dstT(3,3) = 1.0;\n}\n\n#ifdef __PoseManipUtils__with_ROS\nvoid PoseManipUtils::geometry_msgs_Pose_to_eigenmat( const geometry_msgs::Pose& pose, Matrix4d& dstT )\n{\n    Quaterniond q = Quaterniond( pose.orientation.w, pose.orientation.x, pose.orientation.y, pose.orientation.z  );\n\n    dstT = Matrix4d::Zero();\n    dstT.topLeftCorner<3,3>() = q.toRotationMatrix();\n\n    dstT(0,3) = pose.position.x;\n    dstT(1,3) = pose.position.y;\n    dstT(2,3) = pose.position.z;\n    dstT(3,3) = 1.0;\n}\n\n\nvoid PoseManipUtils::eigenmat_to_geometry_msgs_Pose( const Matrix4d& T, geometry_msgs::Pose& pose )\n{\n    assert( T(3,3) == 1 );\n    Quaterniond q( T.topLeftCorner<3,3>() );\n\n    pose.position.x = T(0,3);\n    pose.position.y = T(1,3);\n    pose.position.z = T(2,3);\n    pose.orientation.x = q.x();\n    pose.orientation.y = q.y();\n    pose.orientation.z = q.z();\n    pose.orientation.w = q.w();\n\n}\n#endif\n\nvoid PoseManipUtils::eigenmat_to_raw( const Matrix4d& T, double * quat, double * t)\n{\n  assert( T(3,3) == 1 );\n  Quaterniond q( T.topLeftCorner<3,3>() );\n  quat[0] = q.w();\n  quat[1] = q.x();\n  quat[2] = q.y();\n  quat[3] = q.z();\n  t[0] = T(0,3);\n  t[1] = T(1,3);\n  t[2] = T(2,3);\n}\n\n\nvoid PoseManipUtils::raw_xyzw_to_eigenmat( const double * quat, const double * t, Matrix4d& dstT )\n{\n  Quaterniond q = Quaterniond( quat[3], quat[0], quat[1], quat[2] );\n\n  dstT = Matrix4d::Zero();\n  dstT.topLeftCorner<3,3>() = q.toRotationMatrix();\n\n  dstT(0,3) = t[0];\n  dstT(1,3) = t[1];\n  dstT(2,3) = t[2];\n  dstT(3,3) = 1.0;\n}\n\nvoid PoseManipUtils::raw_xyzw_to_eigenmat( const Vector4d& quat, const Vector3d& t, Matrix4d& dstT )\n{\n  Quaterniond q = Quaterniond( quat(3), quat(0), quat(1), quat(2) );\n\n  dstT = Matrix4d::Zero();\n  dstT.topLeftCorner<3,3>() = q.toRotationMatrix();\n\n  dstT(0,3) = t(0);\n  dstT(1,3) = t(1);\n  dstT(2,3) = t(2);\n  dstT(3,3) = 1.0;\n}\n\nvoid PoseManipUtils::eigenmat_to_raw_xyzw( const Matrix4d& T, double * quat, double * t)\n{\n  assert( T(3,3) == 1 );\n  Quaterniond q( T.topLeftCorner<3,3>() );\n  quat[3] = q.w();\n  quat[0] = q.x();\n  quat[1] = q.y();\n  quat[2] = q.z();\n  t[0] = T(0,3);\n  t[1] = T(1,3);\n  t[2] = T(2,3);\n}\n\n\nvoid PoseManipUtils::rawyprt_to_eigenmat( const double * ypr, const double * t, Matrix4d& dstT )\n{\n  dstT = Matrix4d::Identity();\n  Vector3d eigen_ypr;\n  eigen_ypr << ypr[0], ypr[1], ypr[2];\n  dstT.topLeftCorner<3,3>() = ypr2R( eigen_ypr );\n  dstT(0,3) = t[0];\n  dstT(1,3) = t[1];\n  dstT(2,3) = t[2];\n}\n\n// input ypr must be in degrees.\nvoid PoseManipUtils::rawyprt_to_eigenmat( const Vector3d& eigen_ypr_degrees, const Vector3d& t, Matrix4d& dstT )\n{\n  dstT = Matrix4d::Identity();\n  dstT.topLeftCorner<3,3>() = ypr2R( eigen_ypr_degrees );\n  dstT(0,3) = t(0);\n  dstT(1,3) = t(1);\n  dstT(2,3) = t(2);\n}\n\nvoid PoseManipUtils::eigenmat_to_rawyprt( const Matrix4d& T, double * ypr, double * t)\n{\n  assert( T(3,3) == 1 );\n  Vector3d T_cap_ypr = R2ypr( T.topLeftCorner<3,3>() );\n  ypr[0] = T_cap_ypr(0);\n  ypr[1] = T_cap_ypr(1);\n  ypr[2] = T_cap_ypr(2);\n\n  t[0] = T(0,3);\n  t[1] = T(1,3);\n  t[2] = T(2,3);\n}\n\nvoid PoseManipUtils::eigenmat_to_rawyprt( const Matrix4d& T, Vector3d& ypr, Vector3d& t)\n{\n    assert( T(3,3) == 1 );\n    Vector3d T_cap_ypr = R2ypr( T.topLeftCorner<3,3>() );\n    ypr(0) = T_cap_ypr(0);\n    ypr(1) = T_cap_ypr(1);\n    ypr(2) = T_cap_ypr(2);\n\n    t(0) = T(0,3);\n    t(1) = T(1,3);\n    t(2) = T(2,3);\n}\n\nVector3d PoseManipUtils::R2ypr( const Matrix3d& R)\n{\n  Eigen::Vector3d n = R.col(0);\n  Eigen::Vector3d o = R.col(1);\n  Eigen::Vector3d a = R.col(2);\n\n  Eigen::Vector3d ypr(3);\n  double y = atan2(n(1), n(0));\n  double p = atan2(-n(2), n(0) * cos(y) + n(1) * sin(y));\n  double r = atan2(a(0) * sin(y) - a(1) * cos(y), -o(0) * sin(y) + o(1) * cos(y));\n  ypr(0) = y;\n  ypr(1) = p;\n  ypr(2) = r;\n\n  return ypr / M_PI * 180.0;\n}\n\n\nMatrix3d PoseManipUtils::ypr2R( const Vector3d& ypr)\n{\n  double y = ypr(0) / 180.0 * M_PI;\n  double p = ypr(1) / 180.0 * M_PI;\n  double r = ypr(2) / 180.0 * M_PI;\n\n  // Eigen::Matrix<double, 3, 3> Rz;\n  Matrix3d Rz;\n  Rz << cos(y), -sin(y), 0,\n      sin(y), cos(y), 0,\n      0, 0, 1;\n\n  // Eigen::Matrix<double, 3, 3> Ry;\n  Matrix3d Ry;\n  Ry << cos(p), 0., sin(p),\n      0., 1., 0.,\n      -sin(p), 0., cos(p);\n\n  // Eigen::Matrix<double, 3, 3> Rx;\n  Matrix3d Rx;\n  Rx << 1., 0., 0.,\n      0., cos(r), -sin(r),\n      0., sin(r), cos(r);\n\n  return Rz * Ry * Rx;\n}\n\nvoid PoseManipUtils::prettyprintPoseMatrix( const Matrix4d& M )\n{\n  cout << \"YPR      : \" << R2ypr(  M.topLeftCorner<3,3>() ).transpose() << \"; \";\n  cout << \"Tx,Ty,Tz : \" << M(0,3) << \", \" << M(1,3) << \", \" << M(2,3) << endl;\n}\n\nvoid PoseManipUtils::prettyprintPoseMatrix( const Matrix4d& M, string& return_string )\n{\n   Vector3d ypr;\n   ypr = R2ypr(  M.topLeftCorner<3,3>()  );\n\n  char __tmp[200];\n  snprintf( __tmp, 200, \":YPR(deg)=(%4.2f,%4.2f,%4.2f)  :TxTyTz=(%4.2f,%4.2f,%4.2f)\",  ypr(0), ypr(1), ypr(2), M(0,3), M(1,3), M(2,3) );\n  return_string = string( __tmp );\n}\n\n\nstring PoseManipUtils::prettyprintMatrix4d( const Matrix4d& M )\n{\n   Vector3d ypr;\n   ypr = R2ypr(  M.topLeftCorner<3,3>()  );\n\n  char __tmp[200];\n  snprintf( __tmp, 200, \":YPR(deg)=(%4.3f,%4.3f,%4.3f)  :TxTyTz=(%4.3f,%4.3f,%4.3f)\",  ypr(0), ypr(1), ypr(2), M(0,3), M(1,3), M(2,3) );\n  string return_string = string( __tmp );\n  return return_string;\n}\n\nstring PoseManipUtils::prettyprintMatrix4d_YPR( const Matrix4d& M )\n{\n   Vector3d ypr;\n   ypr = R2ypr(  M.topLeftCorner<3,3>()  );\n\n  char __tmp[200];\n  snprintf( __tmp, 200, \" YPR(deg)=(%4.2f,%4.2f,%4.2f) \",  ypr(0), ypr(1), ypr(2) );\n  string return_string = string( __tmp );\n  return return_string;\n}\n\nstring PoseManipUtils::prettyprintMatrix4d_t( const Matrix4d& M )\n{\n   Vector3d ypr;\n   ypr = R2ypr(  M.topLeftCorner<3,3>()  );\n\n  char __tmp[200];\n  snprintf( __tmp, 200, \" TxTyTz=(%4.2f,%4.2f,%4.2f) \",  M(0,3), M(1,3), M(2,3) );\n  string return_string = string( __tmp );\n  return return_string;\n}\n\n\nvoid PoseManipUtils::vec_to_cross_matrix( const Vector3d& t, Matrix3d& A_x )\n{\n    // Tx = Matrix3d::Zero();\n    A_x << 0, -t(2) , t(1) ,\n          t(2), 0,  -t(0),\n          -t(1), t(0), 0;\n}\n\nvoid PoseManipUtils::vec_to_cross_matrix( double a, double b, double c, Matrix3d& A_x )\n{\n    Vector3d t(a,b,c);\n    vec_to_cross_matrix( t, A_x );\n}\n"
  },
  {
    "path": "src/utils/PoseManipUtils.h",
    "content": "#pragma once\n//\n// This provides utilities for pose manipulation.\n//\n// Author : Manohar Kuse <mpkuse@connect.ust.hk>\n//\n\n#include <iostream>\n#include <string>\n\n\n#include <Eigen/Dense>\n#include <Eigen/Geometry>\n\n\n// uncomment this to compile without ros dependency for this file\n#define __PoseManipUtils__with_ROS 1\n\n#ifdef __PoseManipUtils__with_ROS\n#include <geometry_msgs/Pose.h>\n#endif\n\nusing namespace std;\nusing namespace Eigen;\n\nclass PoseManipUtils\n{\npublic:\n    static void raw_to_eigenmat( const double * quat, const double * t, Matrix4d& dstT );\n    static void eigenmat_to_raw( const Matrix4d& T, double * quat, double * t);\n    static void rawyprt_to_eigenmat( const double * ypr, const double * t, Matrix4d& dstT );\n    static void rawyprt_to_eigenmat( const Vector3d& eigen_ypr_degrees, const Vector3d& t, Matrix4d& dstT );\n\n    static void eigenmat_to_rawyprt( const Matrix4d& T, double * ypr, double * t);\n    static void eigenmat_to_rawyprt( const Matrix4d& T, Vector3d& ypr, Vector3d& t); \n\n    static Vector3d R2ypr( const Matrix3d& R);\n    static Matrix3d ypr2R( const Vector3d& ypr); // input ypr must be in degrees.\n    static void prettyprintPoseMatrix( const Matrix4d& M );\n    static void prettyprintPoseMatrix( const Matrix4d& M, string& return_string );\n\n    static string prettyprintMatrix4d( const Matrix4d& M );\n    static string prettyprintMatrix4d_YPR( const Matrix4d& M );\n    static string prettyprintMatrix4d_t( const Matrix4d& M );\n\n\n    static void raw_xyzw_to_eigenmat( const double * quat, const double * t, Matrix4d& dstT );\n    static void raw_xyzw_to_eigenmat( const Vector4d& quat, const Vector3d& t, Matrix4d& dstT );\n\n    static void eigenmat_to_raw_xyzw( const Matrix4d& T, double * quat, double * t);\n\n    #ifdef __PoseManipUtils__with_ROS\n    static void geometry_msgs_Pose_to_eigenmat( const geometry_msgs::Pose& pose, Matrix4d& dstT );\n    static void eigenmat_to_geometry_msgs_Pose( const Matrix4d& T, geometry_msgs::Pose& pose );\n    #endif\n\n    // Given a point convert it to cross-product matrix. A_x = [ [ 0, -c, -b ], [c,0,-a], [-b,-a,0] ]\n    static void vec_to_cross_matrix( const Vector3d& a, Matrix3d& A_x );\n    static void vec_to_cross_matrix( double a, double b, double c, Matrix3d& A_x );\n\n};\n"
  },
  {
    "path": "src/utils/RawFileIO.cpp",
    "content": "#include \"RawFileIO.h\"\n\n\nvoid RawFileIO::write_image( string fname, const cv::Mat& img)\n{\n    __RawFileIO__write_image_debug_dm( std::cout << \"write_image: \"<< fname << endl );\n    cv::imwrite( (fname).c_str(), img );\n}\n\n\nvoid RawFileIO::write_string( string fname, const string& my_string)\n{\n    __RawFileIO__write_image_debug_dm( std::cout << \"write_string: \"<< fname << endl );\n    std::ofstream outfile( fname );\n    outfile << my_string << endl;\n    outfile.close();\n}\n\n// templated static function canot only exist in header files.\n//  so this was moved to the header file\n// template <typename Derived>\n// void RawFileIO::write_EigenMatrix(const string& filename, const MatrixBase<Derived>& a)\n// {\n//   // string base = string(\"/home/mpkuse/Desktop/bundle_adj/dump/datamgr_mateigen_\");\n//   std::ofstream file(filename);\n//   if( file.is_open() )\n//   {\n//     // file << a.format(CSVFormat) << endl;\n//     file << a << endl;\n//     write_image_debug_dm(std::cout << \"\\033[1;32m\" <<\"Written to file: \"<< filename  << \"\\033[0m\\n\" );\n//   }\n//   else\n//   {\n//     cout << \"\\033[1;31m\" << \"FAIL TO OPEN FILE for writing: \"<< filename << \"\\033[0m\\n\";\n//\n//   }\n// }\n\n\nvoid RawFileIO::write_Matrix2d( const string& filename, const double * D, int nRows, int nCols )\n{\n  // string base = string(\"/home/mpkuse/Desktop/bundle_adj/dump/datamgr_mat2d_\");\n  std::ofstream file(filename);\n  if( file.is_open() )\n  {\n    int c = 0 ;\n    for( int i=0; i<nRows ; i++ )\n    {\n      file << D[c];\n      c++;\n      for( int j=1 ; j<nCols ; j++ )\n      {\n        file << \", \" << D[c] ;\n        c++;\n      }\n      file << \"\\n\";\n    }\n    __RawFileIO__write_image_debug_dm( std::cout << \"\\033[1;32m\" <<\"write_Matrix2d to file: \"<< filename  << \"\\033[0m\\n\" );\n  }\n  else\n  {\n    std::cout << \"\\033[1;31m\" << \"FAIL TO OPEN FILE for writing: \"<< filename << \"\\033[0m\\n\";\n\n  }\n\n}\n\nvoid RawFileIO::write_Matrix1d( const string& filename, const double * D, int n  )\n{\n  std::ofstream file(filename);\n  if( file.is_open() )\n  {\n    file << D[0];\n    for( int i=1 ; i<n ; i++ )\n      file << \", \" << D[i] ;\n    file << \"\\n\";\n    __RawFileIO__write_image_debug_dm(std::cout << \"\\033[1;32m\" <<\"write_Matrix1d: \"<< filename  << \"\\033[0m\\n\");\n  }\n  else\n  {\n    std::cout << \"\\033[1;31m\" << \"FAIL TO OPEN FILE for writing: \"<< filename << \"\\033[0m\\n\";\n\n  }\n\n}\n\n\n\n\nbool RawFileIO::read_eigen_matrix( string filename, MatrixXd& result )\n    {\n    int cols = 0, rows = 0;\n    const int MAXBUFSIZE = ((int) 1e6);\n    double buff[MAXBUFSIZE];\n\n    // Read numbers from file into buffer.\n    __RawFileIO__write_image_debug_dm( std::cout << \"\\033[1;32m\" << \"read_eigen_matrix: \"<< filename << \"\\033[0m\"  );\n    ifstream infile;\n    infile.open(filename);\n    if( !infile ) {\n        cout << \"\\n\\033[1;31m\" << \"failed to open file \" << filename << \"\\033[0m\" << endl;\n        return false;\n    }\n    while (! infile.eof())\n        {\n        string line;\n        getline(infile, line);\n\n        int temp_cols = 0;\n        stringstream stream(line);\n        while(! stream.eof())\n            stream >> buff[cols*rows+temp_cols++];\n\n        if (temp_cols == 0)\n            continue;\n\n        if (cols == 0)\n            cols = temp_cols;\n\n        rows++;\n        }\n\n    infile.close();\n\n    rows--;\n\n    // Populate matrix with numbers.\n    result = MatrixXd::Zero(rows,cols);\n    for (int i = 0; i < rows; i++)\n        for (int j = 0; j < cols; j++)\n            result(i,j) = buff[ cols*i+j ];\n\n    // return result;\n    __RawFileIO__write_image_debug_dm( cout << \"\\tshape=\" << result.rows() << \"x\" << result.cols() << endl; )\n\n    return true;\n    };\n\n\nbool RawFileIO::read_eigen_matrix( string filename, Matrix4d& result )\n    {\n    int cols = 0, rows = 0;\n    const int MAXBUFSIZE = ((int) 2000);\n    double buff[MAXBUFSIZE];\n\n    // Read numbers from file into buffer.\n    __RawFileIO__write_image_debug_dm( std::cout << \"\\033[1;32m\"  << \"read_eigen_matrix: \"<< filename << \"\\033[0m\" );\n    ifstream infile;\n    infile.open(filename);\n    if( !infile ) {\n        cout << \"\\n\\033[1;31m\" << \"failed to open file \" << filename << \"\\033[0m\" << endl;\n        return false;\n    }\n    while (! infile.eof())\n        {\n        string line;\n        getline(infile, line);\n\n        int temp_cols = 0;\n        stringstream stream(line);\n        while(! stream.eof())\n            stream >> buff[cols*rows+temp_cols++];\n\n        if (temp_cols == 0)\n            continue;\n\n        if (cols == 0)\n            cols = temp_cols;\n\n        rows++;\n        }\n\n    infile.close();\n\n    rows--;\n\n    assert( rows==4 && cols==4 && \"\\n[RawFileIO::read_eigen_matrix( string filename, Matrix4d& result )] The input file is not 4x4\" );\n\n    // Populate matrix with numbers.\n    result = Matrix4d::Zero();\n    for (int i = 0; i < rows; i++)\n        for (int j = 0; j < cols; j++)\n            result(i,j) = buff[ cols*i+j ];\n\n    // return result;\n    __RawFileIO__write_image_debug_dm( cout << \"\\tshape=\" << result.rows() << \"x\" << result.cols() << endl; )\n    return true;\n    };\n\n\n\nbool RawFileIO::read_eigen_matrix( string filename, Matrix3d& result )\n{\n    int cols = 0, rows = 0;\n    const int MAXBUFSIZE = ((int) 2000);\n    double buff[MAXBUFSIZE];\n\n    // Read numbers from file into buffer.\n    __RawFileIO__write_image_debug_dm( std::cout << \"\\033[1;32m\"  << \"read_eigen_matrix: \"<< filename << \"\\033[0m\" );\n    ifstream infile;\n    infile.open(filename);\n    if( !infile ) {\n        cout << \"\\n\\033[1;31m\" << \"failed to open file \" << filename << \"\\033[0m\" << endl;\n        return false;\n    }\n    while (! infile.eof())\n        {\n        string line;\n        getline(infile, line);\n\n        int temp_cols = 0;\n        stringstream stream(line);\n        while(! stream.eof())\n            stream >> buff[cols*rows+temp_cols++];\n\n        if (temp_cols == 0)\n            continue;\n\n        if (cols == 0)\n            cols = temp_cols;\n\n        rows++;\n        }\n\n    infile.close();\n\n    rows--;\n\n    assert( rows==3 && cols==3 && \"\\n[RawFileIO::read_eigen_matrix( string filename, Matrix3d& result )] The input file is not 3x3\" );\n\n    // Populate matrix with numbers.\n    result = Matrix3d::Zero();\n    for (int i = 0; i < rows; i++)\n        for (int j = 0; j < cols; j++)\n            result(i,j) = buff[ cols*i+j ];\n\n    // return result;\n    __RawFileIO__write_image_debug_dm( cout << \"\\tshape=\" << result.rows() << \"x\" << result.cols() << endl; )\n    return true;\n}\n\n\nbool RawFileIO::read_eigen_matrix( string filename, VectorXi& result )\n{\n    int cols = 0, rows = 0;\n    const int MAXBUFSIZE = ((int) 2000);\n    double buff[MAXBUFSIZE];\n\n    // Read numbers from file into buffer.\n    __RawFileIO__write_image_debug_dm( std::cout << \"\\033[1;32m\"  << \"read_eigen_matrix: \"<< filename << \"\\033[0m\" );\n    ifstream infile;\n    infile.open(filename);\n    if( !infile ) {\n        cout << \"\\n\\033[1;31m\" << \"failed to open file \" << filename << \"\\033[0m\" << endl;\n        return false;\n    }\n    while (! infile.eof())\n        {\n        string line;\n        getline(infile, line);\n\n        int temp_cols = 0;\n        stringstream stream(line);\n        while(! stream.eof())\n            stream >> buff[cols*rows+temp_cols++];\n\n        if (temp_cols == 0)\n            continue;\n\n        if (cols == 0)\n            cols = temp_cols;\n\n        rows++;\n        }\n\n    infile.close();\n\n    rows--;\n\n    assert( cols==1 && \"\\n[RawFileIO::read_eigen_matrix( string filename, VectorXi& result )] The input file is not row-vector\" );\n\n    // Populate matrix with numbers.\n    result = VectorXi::Zero(rows);\n    for (int i = 0; i < rows; i++)\n        for (int j = 0; j < cols; j++)\n            result(i,j) = (int) buff[ cols*i+j ];\n\n    // return result;\n    __RawFileIO__write_image_debug_dm( cout << \"\\tshape=\" << result.rows() << \"x\" << result.cols() << endl; )\n    return true;\n};\n\n\nbool RawFileIO::read_eigen_matrix( const std::vector<double>& ary, Matrix4d& result )\n{\n    assert( ary.size() == result.rows() * result.cols() && \"[RawFileIO::read_eigen_matrix] size of vector<double> need to be equal to the size of Eigen::Matrix\");\n\n    for( int i=0 ; i<ary.size() ; i++ ) {\n        result(i/4, i%4) = ary[i];\n    }\n}\n\n\nbool RawFileIO::if_file_exist( char * fname )\n{\n  ifstream f(fname);\n  return f.good();\n}\n\nbool RawFileIO::if_file_exist( string fname ) { return if_file_exist( fname.c_str() ); }\n\nbool RawFileIO::is_path_a_directory(const char* path)\n{\n    struct stat buf;\n    stat(path, &buf);\n    return S_ISDIR(buf.st_mode);\n}\n\nbool RawFileIO::is_path_a_directory(const string path) { return is_path_a_directory(path.c_str() ); }\n\nint RawFileIO::exec_cmd( const string& system_cmd ) //< Executes a unix command.\n{\n    cout <<\"\\033[1;33m\"  << system_cmd << \"\\033[0m\" << endl;\n    const int _err_code = system( system_cmd.c_str() );\n    return _err_code;\n}\n\n\n#ifdef WITH_NLOHMANN_JSON\nbool RawFileIO::read_eigen_matrix_fromjson( const json str, MatrixXd&  output )\n{\n    int ncols = str[\"cols\"];\n    int nrows = str[\"rows\"];\n    string data = str[\"data\"];\n    if( ncols > 0 && ncols > 0 )\n        output = MatrixXd::Zero(nrows, ncols );\n    else {\n        cout << \"[RawFileIO::read_eigen_matrix_fromjson] ERROR, nrows and cols should be positive\\n\";\n        return false;\n    }\n\n\n    // split( data, '\\n')\n    vector<string> all_rows = MiscUtils::split( data, '\\n' );\n    if( nrows != all_rows.size() )\n    {\n        cout << \"[RawFileIO::read_eigen_matrix_fromjson] ERROR, requested \" << nrows << \" but actually are \" << all_rows.size() << endl;\n        return false;\n    }\n    for( int r=0 ; r<all_rows.size() ; r++ )\n    {\n        vector<string> all_cols_for_this_row = MiscUtils::split( all_rows[r], ',' );\n        if( ncols != all_cols_for_this_row.size() )\n        {\n            cout << \"[RawFileIO::read_eigen_matrix_fromjson] ERROR, requested \" << ncols << \" but actually are \" << all_cols_for_this_row.size() << \" for row=\" << r << endl;\n            return false;\n        }\n\n        for( int c=0 ; c<all_cols_for_this_row.size() ; c++ )\n        {\n            output(r, c) = std::stod( all_cols_for_this_row[c] );\n        }\n    }\n    return true;\n\n}\n#endif //WITH_NLOHMANN_JSON\n\n\n#ifdef WITH_NLOHMANN_JSON\n// The input json need to be something like:\n//{\n//            \"cols\": 4,\n//            \"rows\": 4,\n//            \"data\": \"0.2857131543876468, -0.2530077727001951, 0.9243132912401226, -0.02953755668229465,\\t\\n-0.9566719337894203, -0.01884313243445668, 0.2905576491157474, 0.2114882406102183,\\t\\n-0.05609638588601445, -0.9672807262182707, -0.2474291659792429, 0.04534835279466286,\\t\\n0, 0, 0, 1\"\n// }\nbool RawFileIO::read_eigen_matrix4d_fromjson( const json str, Matrix4d&  output )\n{\n    output = Matrix4d::Zero();\n    int ncols = str[\"cols\"];\n    int nrows = str[\"rows\"];\n    string data = str[\"data\"];\n\n    if( ncols != 4 || nrows != 4 )\n    {\n        cout << \"[RawFileIO::read_eigen_matrix4d_fromjson] ERROR, you request to convert to Matrix4d however the input json rows and cols != 4x4\\n\";\n        return false;\n    }\n\n\n    // split( data, '\\n')\n    vector<string> all_rows = MiscUtils::split( data, '\\n' );\n    if( nrows != all_rows.size() )\n    {\n        cout << \"[RawFileIO::read_eigen_matrix4d_fromjson] ERROR, requested \" << nrows << \" but actually are \" << all_rows.size() << endl;\n        return false;\n    }\n    for( int r=0 ; r<all_rows.size() ; r++ )\n    {\n        vector<string> all_cols_for_this_row = MiscUtils::split( all_rows[r], ',' );\n        if( ncols != all_cols_for_this_row.size() )\n        {\n            cout << \"[RawFileIO::read_eigen_matrix4d_fromjson] ERROR, requested \" << ncols << \" but actually are \" << all_cols_for_this_row.size() << \" for row=\" << r << endl;\n            return false;\n        }\n\n        for( int c=0 ; c<all_cols_for_this_row.size() ; c++ )\n        {\n            output(r, c) = std::stod( all_cols_for_this_row[c] );\n        }\n    }\n    return true;\n\n}\n#endif //WITH_NLOHMANN_JSON\n\n#ifdef WITH_NLOHMANN_JSON\nbool RawFileIO::read_eigen_vector_fromjson( const json str, VectorXd&  output )\n{\n    int ncols = str[\"cols\"];\n    int nrows = str[\"rows\"];\n    string data = str[\"data\"];\n\n    if( nrows > 0 ) {\n        output = VectorXd::Zero( nrows );\n    }\n    else {\n        cout << \"[RawFileIO::read_eigen_vector_fromjson] ERROR, nrows should be positive\\n\";\n        return false;\n    }\n\n    if( ncols != 1 )\n    {\n        cout << \"[RawFileIO::read_eigen_vector_fromjson] ERROR, you request to convert to VectorXd however the input json cols != 1\\n\";\n        return false;\n    }\n\n    vector<string> all_rows = MiscUtils::split( data, '\\n' );\n    if( nrows != all_rows.size() )\n    {\n        cout << \"[RawFileIO::read_eigen_vector_fromjson] ERROR, requested \" << nrows << \" but actually are \" << all_rows.size() << endl;\n        return false;\n    }\n\n    for( int r=0 ; r<all_rows.size() ; r++ )\n    {\n        vector<string> all_cols_for_this_row = MiscUtils::split( all_rows[r], ',' );\n        if( all_cols_for_this_row.size() != 1 )\n        {\n            cout << \"[RawFileIO::read_eigen_vector_fromjson] ERROR, requested \" << ncols << \" but actually are \" << all_cols_for_this_row.size() << \" for row=\" << r << endl;\n            return false;\n        }\n\n        output( r ) = std::stod( all_cols_for_this_row[0] );\n    }\n    return true;\n\n\n}\n#endif //WITH_NLOHMANN_JSON\n"
  },
  {
    "path": "src/utils/RawFileIO.h",
    "content": "#pragma once\n\n\n#include <iostream>\n#include <string>\n#include <vector>\n#include <fstream>\n#include <queue>\n#include <ostream>\n#include <cstdlib>\n\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n\n#include <Eigen/Core>\n#include <Eigen/Dense>\n#include <Eigen/Geometry>\nusing namespace Eigen;\n#include <opencv2/core/eigen.hpp>\n\nusing namespace std;\n\n#define WITH_NLOHMANN_JSON\n#ifdef WITH_NLOHMANN_JSON\n#include \"nlohmann/json.hpp\"\nusing json = nlohmann::json;\n\n#include \"MiscUtils.h\"\n#endif\n\n\n#include <sys/stat.h> // for is_directory\n\n#define __RawFileIO__write_image_debug_dm( msg ) msg;\n\nclass RawFileIO\n{\npublic:\n    static void write_image( string fname, const cv::Mat& img);\n    static void write_string( string fname, const string& my_string);\n\n    // templated static function canot only exist in header files.\n    template <typename Derived>\n    static void write_EigenMatrix(const string& filename, const MatrixBase<Derived>& a)\n    {\n      // string base = string(\"/home/mpkuse/Desktop/bundle_adj/dump/datamgr_mateigen_\");\n      std::ofstream file(filename);\n      if( file.is_open() )\n      {\n        // file << a.format(CSVFormat) << endl;\n        file << a << endl;\n        __RawFileIO__write_image_debug_dm(std::cout << \"\\033[1;32m\" <<\"write_EigenMatrix: \"<< filename  << \"    size=\" << a.rows() << \"x\" << a.cols() << \"\\033[0m\\n\";)\n      }\n      else\n      {\n        cout << \"\\033[1;31m\" << \"FAIL TO OPEN FILE for writing: \"<< filename << \"\\033[0m\\n\";\n\n      }\n    }\n\n\n    static void write_Matrix2d( const string& filename, const double * D, int nRows, int nCols );\n    static void write_Matrix1d( const string& filename, const double * D, int n  );\n\n    static bool read_eigen_matrix( string filename, MatrixXd& result );\n    static bool read_eigen_matrix( string filename, Matrix4d& result );\n    static bool read_eigen_matrix( string filename, Matrix3d& result );\n    static bool read_eigen_matrix( string filename, VectorXi& result );\n\n    ///< read the flat vector ary as a rowmajor matrix.\n    /// [ 1, 2, 3, 4,5,6...,16 ] ==> [ [1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,16] ]\n    /// TODO: Have a flag to read interpret the 1d array as a colmajor.\n    static bool read_eigen_matrix( const std::vector<double>& ary, Matrix4d& result );\n\n    #ifdef WITH_NLOHMANN_JSON\n    // The input json need to be something like:\n    // A new row is denoted with \\n, and a comma separates elements.\n    //{\n    //            \"cols\": 4,\n    //            \"rows\": 4,\n    //            \"data\": \"0.2857131543876468, -0.2530077727001951, 0.9243132912401226, -0.02953755668229465\\n-0.9566719337894203, -0.01884313243445668, 0.2905576491157474, 0.2114882406102183,\\n-0.05609638588601445, -0.9672807262182707, -0.2474291659792429, 0.04534835279466286\\n0, 0, 0, 1\"\n    // }\n    static bool read_eigen_matrix_fromjson( const json str, MatrixXd&  output );\n    static bool read_eigen_matrix4d_fromjson( const json str, Matrix4d&  output );\n    static bool read_eigen_vector_fromjson( const json str, VectorXd&  output );\n    #endif\n\n\n    static bool if_file_exist( char * fname );\n    static bool if_file_exist( string fname );\n\n    static bool is_path_a_directory(const char* path);\n    static bool is_path_a_directory(const string path);\n\n\n    static int exec_cmd( const string& cmd ); //< Executes a unix command.\n\n};\n"
  },
  {
    "path": "src/utils/RosMarkerUtils.cpp",
    "content": "#include \"RosMarkerUtils.h\"\n\n// cam_size = 1: means basic size. 1.5 will make it 50% bigger.\nvoid RosMarkerUtils::init_camera_marker( visualization_msgs::Marker& marker, float cam_size )\n{\n     marker.header.frame_id = \"world\";\n     marker.header.stamp = ros::Time::now();\n     marker.action = visualization_msgs::Marker::ADD;\n     marker.color.a = .7; // Don't forget to set the alpha!\n     marker.type = visualization_msgs::Marker::LINE_LIST;\n    //  marker.id = i;\n    //  marker.ns = \"camerapose_visual\";\n\n     marker.scale.x = 0.003; //width of line-segments\n     float __vcam_width = 0.07*cam_size;\n     float __vcam_height = 0.04*cam_size;\n     float __z = 0.1*cam_size;\n\n\n\n\n     marker.points.clear();\n     geometry_msgs::Point pt;\n     pt.x = 0; pt.y=0; pt.z=0;\n     marker.points.push_back( pt );\n     pt.x = __vcam_width; pt.y=__vcam_height; pt.z=__z;\n     marker.points.push_back( pt );\n     pt.x = 0; pt.y=0; pt.z=0;\n     marker.points.push_back( pt );\n     pt.x = -__vcam_width; pt.y=__vcam_height; pt.z=__z;\n     marker.points.push_back( pt );\n     pt.x = 0; pt.y=0; pt.z=0;\n     marker.points.push_back( pt );\n     pt.x = __vcam_width; pt.y=-__vcam_height; pt.z=__z;\n     marker.points.push_back( pt );\n     pt.x = 0; pt.y=0; pt.z=0;\n     marker.points.push_back( pt );\n     pt.x = -__vcam_width; pt.y=-__vcam_height; pt.z=__z;\n     marker.points.push_back( pt );\n\n     pt.x = __vcam_width; pt.y=__vcam_height; pt.z=__z;\n     marker.points.push_back( pt );\n     pt.x = -__vcam_width; pt.y=__vcam_height; pt.z=__z;\n     marker.points.push_back( pt );\n     pt.x = -__vcam_width; pt.y=__vcam_height; pt.z=__z;\n     marker.points.push_back( pt );\n     pt.x = -__vcam_width; pt.y=-__vcam_height; pt.z=__z;\n     marker.points.push_back( pt );\n     pt.x = -__vcam_width; pt.y=-__vcam_height; pt.z=__z;\n     marker.points.push_back( pt );\n     pt.x = __vcam_width; pt.y=-__vcam_height; pt.z=__z;\n     marker.points.push_back( pt );\n     pt.x = __vcam_width; pt.y=-__vcam_height; pt.z=__z;\n     marker.points.push_back( pt );\n     pt.x = __vcam_width; pt.y=__vcam_height; pt.z=__z;\n     marker.points.push_back( pt );\n\n\n     // TOSET\n    marker.pose.position.x = 0.;\n    marker.pose.position.y = 0.;\n    marker.pose.position.z = 0.;\n    marker.pose.orientation.x = 0.;\n    marker.pose.orientation.y = 0.;\n    marker.pose.orientation.z = 0.;\n    marker.pose.orientation.w = 1.;\n    // marker.id = i;\n    // marker.ns = \"camerapose_visual\";\n    marker.color.r = 0.2;marker.color.b = 0.;marker.color.g = 0.;\n}\n\nvoid RosMarkerUtils::setpose_to_marker( const Matrix4d& w_T_c, visualization_msgs::Marker& marker )\n{\n    Quaterniond quat( w_T_c.topLeftCorner<3,3>() );\n    marker.pose.position.x = w_T_c(0,3);\n    marker.pose.position.y = w_T_c(1,3);\n    marker.pose.position.z = w_T_c(2,3);\n    marker.pose.orientation.x = quat.x();\n    marker.pose.orientation.y = quat.y();\n    marker.pose.orientation.z = quat.z();\n    marker.pose.orientation.w = quat.w();\n}\n\nvoid RosMarkerUtils::setposition_to_marker( const Vector3d& w_t_c, visualization_msgs::Marker& marker )\n{\n    marker.pose.position.x = w_t_c(0);\n    marker.pose.position.y = w_t_c(1);\n    marker.pose.position.z = w_t_c(2);\n}\n\nvoid RosMarkerUtils::setposition_to_marker( const Vector4d& w_t_c, visualization_msgs::Marker& marker )\n{\n    marker.pose.position.x = w_t_c(0) / w_t_c(3); ;\n    marker.pose.position.y = w_t_c(1) / w_t_c(3); ;\n    marker.pose.position.z = w_t_c(2) / w_t_c(3); ;\n}\n\nvoid RosMarkerUtils::setposition_to_marker( float x, float y, float z, visualization_msgs::Marker& marker )\n{\n    marker.pose.position.x = x;\n    marker.pose.position.y = y;\n    marker.pose.position.z = z;\n}\n\nvoid RosMarkerUtils::setcolor_to_marker( float r, float g, float b, visualization_msgs::Marker& marker  )\n{\n    assert( r>=0. && r<=1.0 && g>=0. && g<=1.0 && b>=0 && b<=1.0 );\n    marker.color.a = 1.0;\n    marker.color.r = r;\n    marker.color.g = g;\n    marker.color.b = b;\n}\n\nvoid RosMarkerUtils::setcolor_to_marker( float r, float g, float b, float a, visualization_msgs::Marker& marker  )\n{\n    assert( r>=0. && r<=1.0 && g>=0. && g<=1.0 && b>=0 && b<=1.0 && a>0. && a<=1.0);\n    marker.color.a = a;\n    marker.color.r = r;\n    marker.color.g = g;\n    marker.color.b = b;\n}\n\nvoid RosMarkerUtils::init_text_marker( visualization_msgs::Marker &marker )\n{\n    marker.header.frame_id = \"world\";\n    marker.header.stamp = ros::Time::now();\n    marker.action = visualization_msgs::Marker::ADD;\n    marker.color.a = .8; // Don't forget to set the alpha!\n    marker.type = visualization_msgs::Marker::TEXT_VIEW_FACING;\n\n    marker.scale.x = 1.; //not in use\n    marker.scale.y = 1.; //not in use\n    marker.scale.z = 1.;\n\n    //// Done . no need to edit firther\n    marker.pose.position.x = 0.;\n    marker.pose.position.y = 0.;\n    marker.pose.position.z = 0.;\n    marker.pose.orientation.x = 0.;\n    marker.pose.orientation.y = 0.;\n    marker.pose.orientation.z = 0.;\n    marker.pose.orientation.w = 1.;\n    // marker.id = i;\n    // marker.ns = \"camerapose_visual\";\n    marker.color.r = 0.2;marker.color.b = 0.;marker.color.g = 0.;\n\n}\n\nvoid RosMarkerUtils::init_line_strip_marker( visualization_msgs::Marker &marker )\n{\n    marker.header.frame_id = \"world\";\n    marker.header.stamp = ros::Time::now();\n    marker.action = visualization_msgs::Marker::ADD;\n    marker.color.a = .8; // Don't forget to set the alpha!\n    marker.type = visualization_msgs::Marker::LINE_STRIP;\n\n    marker.scale.x = 0.02;\n\n    marker.points.clear();\n\n    //// Done . no need to edit firther\n    marker.pose.position.x = 0.;\n    marker.pose.position.y = 0.;\n    marker.pose.position.z = 0.;\n    marker.pose.orientation.x = 0.;\n    marker.pose.orientation.y = 0.;\n    marker.pose.orientation.z = 0.;\n    marker.pose.orientation.w = 1.;\n    // marker.id = i;\n    // marker.ns = \"camerapose_visual\";\n    marker.color.r = 0.2;marker.color.b = 0.;marker.color.g = 0.;\n\n}\n\nvoid RosMarkerUtils::init_line_marker( visualization_msgs::Marker &marker )\n{\n    marker.header.frame_id = \"world\";\n    marker.header.stamp = ros::Time::now();\n    marker.action = visualization_msgs::Marker::ADD;\n    marker.color.a = .8; // Don't forget to set the alpha!\n    marker.type = visualization_msgs::Marker::LINE_LIST;\n\n    marker.scale.x = 0.02;\n\n    marker.points.clear();\n\n    //// Done . no need to edit firther\n    marker.pose.position.x = 0.;\n    marker.pose.position.y = 0.;\n    marker.pose.position.z = 0.;\n    marker.pose.orientation.x = 0.;\n    marker.pose.orientation.y = 0.;\n    marker.pose.orientation.z = 0.;\n    marker.pose.orientation.w = 1.;\n    // marker.id = i;\n    // marker.ns = \"camerapose_visual\";\n    marker.color.r = 0.2;marker.color.b = 0.;marker.color.g = 0.;\n\n}\n\nvoid RosMarkerUtils::init_line_marker( visualization_msgs::Marker &marker, const Vector3d& p1, const Vector3d& p2 )\n{\n    marker.header.frame_id = \"world\";\n    marker.header.stamp = ros::Time::now();\n    marker.action = visualization_msgs::Marker::ADD;\n    marker.color.a = .8; // Don't forget to set the alpha!\n    marker.type = visualization_msgs::Marker::LINE_LIST;\n\n    marker.scale.x = 0.02;\n\n    marker.points.clear();\n    geometry_msgs::Point pt;\n    pt.x = p1(0);\n    pt.y = p1(1);\n    pt.z = p1(2);\n    marker.points.push_back( pt );\n    pt.x = p2(0);\n    pt.y = p2(1);\n    pt.z = p2(2);\n    marker.points.push_back( pt );\n\n    //// Done . no need to edit firther\n    marker.pose.position.x = 0.;\n    marker.pose.position.y = 0.;\n    marker.pose.position.z = 0.;\n    marker.pose.orientation.x = 0.;\n    marker.pose.orientation.y = 0.;\n    marker.pose.orientation.z = 0.;\n    marker.pose.orientation.w = 1.;\n    // marker.id = i;\n    // marker.ns = \"camerapose_visual\";\n    marker.color.r = 0.2;marker.color.b = 0.;marker.color.g = 0.;\n\n}\n\n\nvoid RosMarkerUtils::init_points_marker( visualization_msgs::Marker &marker )\n{\n    marker.header.frame_id = \"world\";\n    marker.header.stamp = ros::Time::now();\n    marker.action = visualization_msgs::Marker::ADD;\n    marker.color.a = .8; // Don't forget to set the alpha!\n    marker.type = visualization_msgs::Marker::POINTS;\n\n    marker.scale.x = 0.04;\n    marker.scale.y = 0.04;\n\n    marker.points.clear();\n\n    //// Done . no need to edit firther\n    marker.pose.position.x = 0.;\n    marker.pose.position.y = 0.;\n    marker.pose.position.z = 0.;\n    marker.pose.orientation.x = 0.;\n    marker.pose.orientation.y = 0.;\n    marker.pose.orientation.z = 0.;\n    marker.pose.orientation.w = 1.;\n    // marker.id = i;\n    // marker.ns = \"camerapose_visual\";\n    marker.color.r = 0.2;marker.color.b = 0.;marker.color.g = 0.;\n\n}\n\n\n\n\nvoid RosMarkerUtils::add_point_to_marker( float x, float y, float z, visualization_msgs::Marker& marker, bool clear_prev_points )\n{\n    if( clear_prev_points )\n        marker.points.clear();\n\n    geometry_msgs::Point pt;\n    pt.x = x; pt.y = y; pt.z = z;\n    marker.points.push_back( pt );\n}\n\nvoid RosMarkerUtils::add_point_to_marker( const Vector3d& X, visualization_msgs::Marker& marker, bool clear_prev_points )\n{\n    if( clear_prev_points )\n        marker.points.clear();\n\n    geometry_msgs::Point pt;\n    pt.x = X(0); pt.y = X(1); pt.z = X(2);\n    marker.points.push_back( pt );\n}\n\nvoid RosMarkerUtils::add_point_to_marker( const Vector4d& X, visualization_msgs::Marker& marker, bool clear_prev_points )\n{\n    if( clear_prev_points )\n        marker.points.clear();\n\n    geometry_msgs::Point pt;\n    assert( abs(X(3)) > 1E-5 );\n    pt.x = X(0)/X(3); pt.y = X(1)/X(3); pt.z = X(2)/X(3);\n    marker.points.push_back( pt );\n}\n\nvoid RosMarkerUtils::add_points_to_marker( const MatrixXd& X, visualization_msgs::Marker& marker, bool clear_prev_points ) //X : 3xN or 4xN.\n{\n    assert( (X.rows() == 3 || X.rows() == 4) && \"[RosMarkerUtils::add_points_to_marker] X need to of size 3xN or 4xN\\n\" );\n    geometry_msgs::Point pt;\n\n    if( clear_prev_points )\n        marker.points.clear();\n\n\n    for( int i=0 ; i<X.cols() ; i++ ) {\n        if( X.rows() == 3 ) {\n            pt.x = X(0,i); pt.y = X(1,i); pt.z = X(2,i);\n        }\n        else {\n            pt.x = X(0,i) / X(3,i); pt.y = X(1,i) / X(3,i); pt.z = X(2,i) / X(3,i);\n        }\n        marker.points.push_back( pt );\n    }\n}\n"
  },
  {
    "path": "src/utils/RosMarkerUtils.h",
    "content": "#pragma once\n\n//\n// This provides utilities creating ros markers\n//\n// Author : Manohar Kuse <mpkuse@connect.ust.hk>\n// Notes: http://wiki.ros.org/rviz/DisplayTypes/Marker\n\n#include <iostream>\n#include <string>\n\n\n#include <Eigen/Dense>\n#include <Eigen/Geometry>\n\n\n#include <ros/ros.h>\n#include <ros/package.h>\n#include <geometry_msgs/Pose.h>\n#include <geometry_msgs/PoseWithCovariance.h>\n#include <geometry_msgs/PoseStamped.h>\n#include <geometry_msgs/Point32.h>\n#include <geometry_msgs/Point.h>\n#include <visualization_msgs/Marker.h>\n#include <visualization_msgs/MarkerArray.h>\n\n\nusing namespace std;\nusing namespace Eigen;\n\n\nclass RosMarkerUtils\n{\npublic:\n    ////////////////// INIT /////////////////////////\n\n\n    /// Initialize a camera with a few lines. You need to set the `ns` and `id` before publishing.\n    static void init_camera_marker( visualization_msgs::Marker& marker, float cam_size );\n    static void init_text_marker( visualization_msgs::Marker &marker );\n    static void init_line_strip_marker( visualization_msgs::Marker &marker );\n    static void init_line_marker( visualization_msgs::Marker &marker );\n    static void init_line_marker( visualization_msgs::Marker &marker, const Vector3d& p1, const Vector3d& p2 );\n    static void init_points_marker( visualization_msgs::Marker &marker );\n\n    //////////////// SET //////////////////////////\n    static void setpose_to_marker( const Matrix4d& w_T_c, visualization_msgs::Marker& marker );\n    static void setposition_to_marker( const Vector3d& w_t_c, visualization_msgs::Marker& marker );\n    static void setposition_to_marker( const Vector4d& w_t_c, visualization_msgs::Marker& marker );\n    static void setposition_to_marker( float x, float y, float z, visualization_msgs::Marker& marker );\n    static void setcolor_to_marker( float r, float g, float b, visualization_msgs::Marker& marker  );\n    static void setcolor_to_marker( float r, float g, float b, float a, visualization_msgs::Marker& marker  );\n\n    //////////////// Add Points ////////////////////\n    static void add_point_to_marker( float x, float y, float z, visualization_msgs::Marker& marker, bool clear_prev_points=true );\n    static void add_point_to_marker( const Vector3d& X, visualization_msgs::Marker& marker, bool clear_prev_points=true );\n    static void add_point_to_marker( const Vector4d& X, visualization_msgs::Marker& marker, bool clear_prev_points=true );\n    static void add_points_to_marker( const MatrixXd& X, visualization_msgs::Marker& marker, bool clear_prev_points=true ); //X : 3xN or 4xN.\n\n};\n"
  },
  {
    "path": "src/utils/SafeQueue.h",
    "content": "#pragma once\n\n#include <queue>\n#include <mutex>\n#include <condition_variable>\n\n// A threadsafe-queue. - https://stackoverflow.com/questions/15278343/c11-thread-safe-queue\ntemplate <class T>\nclass SafeQueue\n{\npublic:\n  SafeQueue(void)\n    : q()\n    , m()\n    , c()\n  {}\n\n  ~SafeQueue(void)\n  {}\n\n  // Add an element to the queue.\n  void enqueue(T t)\n  {\n    std::lock_guard<std::mutex> lock(m);\n    q.push(t);\n    c.notify_one();\n  }\n\n\n\n  // Get the \"front\"-element.\n  // If the queue is empty, wait till a element is avaiable.\n  T dequeue(void)\n  {\n    std::unique_lock<std::mutex> lock(m);\n    while(q.empty())\n    {\n      // release lock as long as the wait and reaquire it afterwards.\n      c.wait(lock);\n    }\n    T val = q.front();\n    q.pop();\n    return val;\n  }\n\n  ////// added my mpkuse\n\n  // return size\n  int size()\n  {\n    std::lock_guard<std::mutex> lock(m);\n    int n = q.size();\n    c.notify_one();\n    return n;\n  }\n\n  // Add an element to the queue.\n  void push(T t)\n  {\n    std::lock_guard<std::mutex> lock(m);\n    q.push(t);\n    c.notify_one();\n  }\n\n  // Get the \"front\"-element.\n  // If the queue is empty, wait till a element is avaiable.\n  T pop(void)\n  {\n    std::unique_lock<std::mutex> lock(m);\n    while(q.empty())\n    {\n      // release lock as long as the wait and reaquire it afterwards.\n      c.wait(lock);\n    }\n    T val = q.front();\n    q.pop();\n    return val;\n  }\n\n  // most recently added element\n  T back()\n  {\n    assert( size() > 0 && \"SafeQueue: You tried to SafeQueue::front(), but the queue was empty. This operation is only allowed when there are elements in the queue.  \");\n    std::lock_guard<std::mutex> lock(m);\n    T val = q.back();\n    c.notify_one();\n    return val;\n  }\n\n  // 1st element in queue\n  T front()\n  {\n    assert( size() > 0 && \"SafeQueue: You tried to SafeQueue::front(), but the queue was empty. This operation is only allowed when there are elements in the queue.  \");\n    std::lock_guard<std::mutex> lock(m);\n    T val = q.front();\n    c.notify_one();\n    return val;\n  }\n\nprivate:\n  std::queue<T> q;\n  mutable std::mutex m;\n  std::condition_variable c;\n};\n"
  },
  {
    "path": "src/utils/TermColor.h",
    "content": "#pragma once\n\n/**\n\n// TODO Work in progress\n// https://stackoverflow.com/questions/2616906/how-do-i-output-coloured-text-to-a-linux-terminal\n\nblack        30         40\nred          31         41\ngreen        32         42\nyellow       33         43\nblue         34         44\nmagenta      35         45\ncyan         36         46\nwhite        37         47\n\nreset             0  (everything back to normal)\nbold/bright       1  (often a brighter shade of the same colour)\nunderline         4\ninverse           7  (swap foreground and background colours)\nbold/bright off  21\nunderline off    24\ninverse off      27\n\neg:\ncout << \"\\033[1;31mbold red text\\033[0m\\n\";\n\n\n\n*/\n#include <iostream>\n#include <sstream>\n#include <string>\n\nclass TermColor\n{\npublic:\n    static std::string RED() { return compose( TermColor::BG_RED );  }\n    static std::string GREEN() { return compose( TermColor::BG_GREEN );  }\n    static std::string YELLOW() { return compose( TermColor::BG_YELLOW );  }\n    static std::string BLUE() { return compose( TermColor::BG_BLUE );  }\n    static std::string MAGENTA() { return compose( TermColor::BG_MAGENTA );  }\n    static std::string CYAN() { return compose( TermColor::BG_CYAN );  }\n    static std::string WHITE() { return compose( TermColor::BG_WHITE );  }\n\n    static std::string iRED() { return compose( TermColor::BG_RED, true );  }\n    static std::string iGREEN() { return compose( TermColor::BG_GREEN, true );  }\n    static std::string iYELLOW() { return compose( TermColor::BG_YELLOW, true );  }\n    static std::string iBLUE() { return compose( TermColor::BG_BLUE, true );  }\n    static std::string iMAGENTA() { return compose( TermColor::BG_MAGENTA, true );  }\n    static std::string iCYAN() { return compose( TermColor::BG_CYAN, true );  }\n    static std::string iWHITE() { return compose( TermColor::BG_WHITE, true );  }\n\n    static std::string RESET()\n    {\n        std::stringstream buffer;\n        buffer << \"\\033[\" << CTRL_RESET << \"m\";\n        return buffer.str();\n    }\n\n    // modifier : bold, underline etc.\n    // color : FG_RED, ..., BG_RED etc\n    static std::string compose( const int modifier, const int color, bool invert=false )\n    {\n\n        std::stringstream buffer;\n        if( !invert )\n            buffer << \"\\033[\" << modifier << \";\" << color << \"m\";\n        else\n            buffer << \"\\033[\" << CTRL_INVERSE << \";\" << modifier << \";\" << color << \"m\";\n        return buffer.str();\n    }\n\n    static std::string compose( const int color, bool invert=false )\n    {\n        std::stringstream buffer;\n        if( !invert )\n            buffer << \"\\033[\" << color << \"m\";\n        else\n            buffer << \"\\033[\" << CTRL_INVERSE << \";\" << color << \"m\";\n        return buffer.str();\n    }\n\n    static const int BG_BLACK = 30;\n    static const int BG_RED = 31;\n    static const int BG_GREEN = 32;\n    static const int BG_YELLOW = 33;\n    static const int BG_BLUE = 34;\n    static const int BG_MAGENTA = 35;\n    static const int BG_CYAN = 36;\n    static const int BG_WHITE = 37;\n\n    static const int FG_BLACK = 40;\n    static const int FG_RED = 41;\n    static const int FG_GREEN = 42;\n    static const int FG_YELLOW = 43;\n    static const int FG_BLUE = 44;\n    static const int FG_MAGENTA = 45;\n    static const int FG_CYAN = 46;\n    static const int FG_WHITE = 47;\n\n    static const int CTRL_RESET = 0;\n    static const int CTRL_BOLD = 1;\n    static const int CTRL_UNDERLINE = 4;\n    static const int CTRL_INVERSE = 7; //(swap foreground and background colours)\n    static const int CTRL_BOLD_OFF = 21;\n    static const int CTRL_UNDERLINE_OFF = 24;\n    static const int CTRL_INVERSE_OFF = 27; //(swap foreground and background colours)\n};\n"
  },
  {
    "path": "src/utils/camera_geometry_usage.cpp",
    "content": "// Sample usage for class CameraGeometry.h/MonoGeometry and CameraGeometry.h/StereoGeometry\n// These classes abstractout the Stereo Geometry, Undistort etc. They can be used\n// with any of the Camodocal cameras ie. Mei, Kannala-brandt, pinhole (ofcourse).\n//  The whole idea of the abstract Camera clases and Geometry class is to make the\n//  codebase truely object oriented and the core geometry abstracted. Hopefully\n//  all this will help to develop more higher level applications faster.\n\n// YONGYEN'S METHOD TO CORRECT THE STEREO-EXTRINSIC WHOLLY CONTAINED IN THIS FILE\n// implements Yonggen Ling's method\n// Y. Ling and S. Shen, \"High-precision online markerless stereo extrinsic calibration,\" 2016 IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS), Daejeon, 2016, pp. 1771-1778.\n\n// minimize_{R,t} \\sum_i || (f'_i)^T E f_i || , where R,t == right_T_left.\n//           a) E is the Essential matrix E := [t]_x R\n//           b) f and f' are point feature matches (f from left f from right) in normalized image co-ordinates\n\n#include <iostream>\n#include <string>\n\n#include \"camodocal/camera_models/Camera.h\"\n#include \"camodocal/camera_models/CameraFactory.h\"\n#include \"camodocal/camera_models/CataCamera.h\"\n#include \"camodocal/camera_models/EquidistantCamera.h\"\n\n//opencv\n#include <opencv2/core/core.hpp>\n#include <opencv2/highgui/highgui.hpp>\n#include <opencv2/calib3d.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n#include \"../utils/MiscUtils.h\"\n#include \"../utils/ElapsedTime.h\"\n#include \"../utils/PoseManipUtils.h\"\n#include \"../utils/TermColor.h\"\n#include \"../utils/CameraGeometry.h\"\n#include \"../utils/RawFileIO.h\"\n\n#include \"gms_matcher.h\"\n\n#include <assert.h>\n\n#include <ceres/ceres.h>\nusing namespace ceres;\n\n\nvoid point_feature_matches( const cv::Mat& imleft_undistorted, const cv::Mat& imright_undistorted,\n                            MatrixXd& u, MatrixXd& ud )\n{\n    ElapsedTime timer;\n\n\n    //\n    // Point feature and descriptors extract\n    std::vector<cv::KeyPoint> kp1, kp2;\n    cv::Mat d1, d2; //< descriptors\n\n    cv::Ptr<cv::ORB> orb = cv::ORB::create(10000);\n    orb->setFastThreshold(0);\n\n    timer.tic();\n    orb->detectAndCompute(imleft_undistorted, cv::Mat(), kp1, d1);\n    orb->detectAndCompute(imright_undistorted, cv::Mat(), kp2, d2);\n    cout << \"2X detectAndCompute(ms) : \" << timer.toc_milli() << endl;\n    // std::cout << \"d1 \" << MiscUtils::cvmat_info( d1 ) << std::endl;\n    // std::cout << \"d2 \" << MiscUtils::cvmat_info( d2 ) << std::endl;\n\n    //plot\n    // cv::Mat dst_left, dst_right;\n    // MatrixXd e_kp1, e_kp2;\n    // MiscUtils::keypoint_2_eigen( kp1, e_kp1 );\n    // MiscUtils::keypoint_2_eigen( kp2, e_kp2 );\n    // MiscUtils::plot_point_sets( imleft_undistorted, e_kp1, dst_left, cv::Scalar(0,0,255), false );\n    // MiscUtils::plot_point_sets( imright_undistorted, e_kp2, dst_right, cv::Scalar(0,0,255), false );\n    // cv::imshow( \"dst_left\", dst_left );\n    // cv::imshow( \"dst_right\", dst_right );\n\n    //\n    // Point feature matching\n    cv::BFMatcher matcher(cv::NORM_HAMMING); // TODO try FLANN matcher here.\n    vector<cv::DMatch> matches_all, matches_gms;\n    timer.tic();\n    matcher.match(d1, d2, matches_all);\n    std::cout << \"BFMatcher : npts = \" << matches_all.size() << std::endl;\n    std::cout << \"BFMatcher took (ms) : \"<< timer.toc_milli() << std::endl;\n\n\n    // gms_matcher\n    timer.tic();\n    std::vector<bool> vbInliers;\n    gms_matcher gms(kp1, imleft_undistorted.size(), kp2, imright_undistorted.size(), matches_all);\n    int num_inliers = gms.GetInlierMask(vbInliers, false, false);\n    cout << \"Got total gms matches \" << num_inliers << \" matches.\" << endl;\n    cout << \"GMSMatcher took (ms) \" << timer.toc_milli() << std::endl;\n\n    // collect matches\n    for (size_t i = 0; i < vbInliers.size(); ++i)\n    {\n        if (vbInliers[i] == true)\n        {\n            matches_gms.push_back(matches_all[i]);\n        }\n    }\n    // MatrixXd M1, M2;\n    MiscUtils::dmatch_2_eigen( kp1, kp2, matches_gms, u, ud, true );\n\n\n}\n\n\n\n// Orthonormalization is needed because the optimization variable translation is unit normalized\n// and to preserve this property we need to define the '+' operation for it. Note that it can\n// only more in tangent space of the sphere. So graph schmit-orthonormalization gives out a basis\n// vector at this point. Ofcourse this is not a unique solution. See Fig. 3 in yonggen's iros2016 paper.\nclass UnitVectorParameterization : public ceres::LocalParameterization {\npublic:\n    UnitVectorParameterization() {}\n    virtual ~UnitVectorParameterization() {}\n\n    virtual bool Plus(const double* x,\n                      const double* delta,\n                      double* x_plus_delta) const\n    {\n        // Compute T (tangent space)\n        // tangent space is a function of x\n        double m[5], n[5];\n        bool statis = gram_schmidt_orthonormalization( x, m, n );\n        if( !statis )\n            return false;\n\n\n        // x_new := x + T*[ delta[0]; delta[1] ]\n        x_plus_delta[0] = x[0] + m[0]*delta[0] + n[0]*delta[1];\n        x_plus_delta[1] = x[1] + m[1]*delta[0] + n[1]*delta[1];\n        x_plus_delta[2] = x[2] + m[2]*delta[0] + n[2]*delta[1];\n        return true;\n    }\n\n    virtual bool ComputeJacobian(const double* x,\n                                 double* jacobian) const\n    {\n        // Compute T (tangent space)\n        // tangent space is a function of x\n        double m[5], n[5];\n        bool statis = gram_schmidt_orthonormalization( x, m, n );\n        if( !statis )\n            return false;\n\n        // \\del(x') / \\del(a) = [  T_{0,0}; T_{1,0}  ]\n\n        // \\del(x') / \\del(b) = [  T_{0,1}; T_{1,1}  ]\n\n\n        // jacobian = [ concate above 2 ]\n        jacobian[0] = m[0]; jacobian[1] = n[0];\n        jacobian[2] = m[1]; jacobian[3] = n[1];\n        jacobian[4] = m[2]; jacobian[5] = n[2];\n        return true;\n\n    }\n    virtual int GlobalSize() const { return 3; } // TODO : Generalize.\n    virtual int LocalSize() const { return 2; }\n\n\n\n    // This function return 2 vectors m, n each of dimension equal to x (ie. 3) such that x,m,n are orthogonal to each other.\n    const bool gram_schmidt_orthonormalization( const double * _x , double * _m, double * _n, int len=3 ) const\n    {\n        VectorXd xp = VectorXd::Zero( len );\n        for( int i=0 ; i<len; i++ ) xp(i) = _x[i];\n\n        // xp = xp / xp.norm();\n\n        // equation of tangent-plane : x_p * x  + y_p * y + z_p * z  = 1,\n        // where (xp,yp,zp) is a known point on the sphere,. point passing through (x_p,y_p,z_p) and\n        // plane normal vector direction as (x_p, y_p, z_p).\n\n        // assert( abs(xp.norm()-1.) < 1e-7 );\n        // if( abs(xp(2)) < 1E-5 )\n            // return false;\n\n        VectorXd m = VectorXd::Zero(len); //< a point on tangent plane.\n        m << 10., -7, 1.0 -( xp(0)*10. + xp(1)*(-7.) )/xp(2); //< assuming xp(2) is non-zero, TODO ideally should divide by the maximum of {xp(0), xp(1), xp(2) }, the points will change accordingly\n\n        VectorXd n = VectorXd::Zero(len); //< another point on tangent place.\n        n << -12., 5, 1.0-( xp(0)*(-12.) + xp(1)*(5.) )/xp(2); //< assuming xp(2) is non-zero\n\n        m = m - xp;\n        m /= m.norm(); //< unit vector in the direction of m\n\n        n = n - xp;\n        n /= n.norm(); //< unit vector in the direction of n\n\n        m = m - proj(xp, m);  // projection of xp in the direction of m\n        m = m / m.norm();\n        n = n - proj( xp, n ) - proj( m, n );\n        n = n / n.norm();\n\n        // m, n are the 2 basis vectors.\n        // cout << \"m\" << m << endl;\n        // cout << \"n\" << n << endl;\n        for( int i=0 ; i<len; i++ ) {\n            _m[i] = m(i);\n            _n[i] = n(i);\n        }\n        return true;\n\n\n    }\n\n    VectorXd proj( const VectorXd& u, const VectorXd& v ) const\n    {\n        return u * ( u.dot(v) / u.dot( u ) );\n    }\n\n\n};\n\n\nclass YonggenResidue {\npublic:\n  YonggenResidue( const Vector3d& Xi, const Vector3d& Xid ) : Xi(Xi), Xid(Xid)\n  {\n    // this->Xi = Xi;\n    // this->Xid = Xid;\n  }\n\n  template <typename T>\n  bool operator()( const T* const q, const T* const t , T* residual ) const {\n    // Optimization variables\n    Quaternion<T> eigen_q( q[0], q[1], q[2], q[3] );\n\n    Eigen::Matrix<T,3,3> eigen_tx;\n    eigen_tx << T(0.0), -t[2] , t[1] ,\n                t[2], T(0.0),  -t[0],\n                -t[1], t[0], T(0.0);\n\n\n    Eigen::Matrix<T,3,3> essential_matrix;\n    essential_matrix = eigen_tx * eigen_q.toRotationMatrix()  ;\n\n\n    // Known Constant\n    Eigen::Matrix<T,3,1> eigen_Xi;\n    Eigen::Matrix<T,3,1> eigen_Xid;\n    eigen_Xi << T(Xi(0)), T(Xi(1)), T(Xi(2));\n    eigen_Xid << T(Xid(0)), T(Xid(1)), T(Xid(2));\n\n\n\n    // Error term\n\n    Eigen::Matrix<T,1,1> e;\n    // e = eigen_Xi - (  eigen_q.toRotationMatrix() * eigen_Xid + eigen_t );\n    e = eigen_Xid.transpose() * essential_matrix * eigen_Xi;\n    // e = eigen_Xi.transpose() * (essential_matrix * eigen_Xid);\n\n\n    residual[0] = e(0);\n    // residual[1] = e(1);\n    // residual[2] = e(2);\n\n    return true;\n  }\n\n\n\n  static ceres::CostFunction* Create(const Vector3d& _Xi, const Vector3d& _Xid)\n  {\n    return ( new ceres::AutoDiffCostFunction<YonggenResidue,1,4,3>\n      (\n        new YonggenResidue(_Xi,_Xid)\n      )\n    );\n  }\n\nprivate:\n  Vector3d Xi, Xid;\n};\n\n\nvoid nudge_extrinsics( const cv::Mat& imleft_undistorted, const cv::Mat& imright_undistorted,\n                        const Matrix3d& K_new, const Matrix4d& right_T_left, Matrix4d& optimized_right_T_left )\n{\n    // implements Yonggen Ling's method\n    // Y. Ling and S. Shen, \"High-precision online markerless stereo extrinsic calibration,\" 2016 IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS), Daejeon, 2016, pp. 1771-1778.\n\n    // minimize_{R,t} \\sum_i || (f'_i)^T E f_i || , where R,t == right_T_left.\n    //           a) E is the Essential matrix E := [t]_x R\n    //           b) f and f' are point feature matches (f from left f from right) in normalized image co-ordinates\n\n\n    //\n    // Step-1 : Match point features from `imleft_undistorted, imright_undistorted`\n    MatrixXd u, ud; //< 3xN, ie. image co-ordinates represented in homogeneous cord system.\n    point_feature_matches( imleft_undistorted, imright_undistorted, u, ud );\n    cv::Mat dst;\n    MiscUtils::plot_point_pair( imleft_undistorted, u, -1, imright_undistorted, ud, -1, dst, 0 );\n    cv::imshow( \"gms_matches\", dst );\n\n\n\n    //\n    // Step-2 : In normalized co-ordinates : inv(K_new) * f' and inv(K_inv) * f\n    MatrixXd f = K_new.inverse() * u;\n    MatrixXd fd = K_new.inverse() * ud;\n\n\n    //\n    // Step-3 : Setup CERES problem\n\n    // 3.1 : Initial Guess\n    double T_cap_q[10], T_cap_t[10];\n    PoseManipUtils::eigenmat_to_raw( right_T_left, T_cap_q, T_cap_t );\n    double n_norm = sqrt( T_cap_t[0]*T_cap_t[0] + T_cap_t[1]*T_cap_t[1] + T_cap_t[2]*T_cap_t[2] );\n    T_cap_t[0] /= n_norm;\n    T_cap_t[1] /= n_norm;\n    T_cap_t[2] /= n_norm;\n\n    cout << \"CERES Inital Guess : \" << PoseManipUtils::prettyprintMatrix4d( right_T_left ) << endl;\n    cout << \"CERES Initial Guess n_norm: \" << n_norm << endl;\n    cout << \"CERES Initial Guess T_cap_t: \" << T_cap_t[0] << \" \" << T_cap_t[1] << \" \" << T_cap_t[2] << endl;\n\n\n    // 3.2 : Error Terms\n    ceres::Problem problem;\n    cout << \"CERES #residues : \" << f.cols() << endl;\n    for( int i=0 ; i<f.cols() ; i++ )\n    {\n        int r = rand() % f.cols();\n        CostFunction* cost_function = YonggenResidue::Create( f.col(r).head(3), fd.col(r).head(3) );\n        // problem.AddResidualBlock( cost_function, NULL, T_cap_q, T_cap_t );\n        problem.AddResidualBlock( cost_function, new ceres::HuberLoss(0.001), T_cap_q, T_cap_t );\n    }\n\n\n    // 3.3 : Local Parameterization\n    ceres::LocalParameterization *quaternion_parameterization = new ceres::QuaternionParameterization;\n    // ceres::LocalParameterization *hv_parameterization = new ceres::HomogeneousVectorParameterization(3);\n    ceres::LocalParameterization *hv_parameterization = new UnitVectorParameterization();\n    problem.SetParameterization( T_cap_q, quaternion_parameterization );\n    problem.SetParameterization( T_cap_t, hv_parameterization );\n\n    //\n    // Step-4 : Solve\n    Solver::Options options;\n    options.minimizer_progress_to_stdout = false;\n    // options.minimizer_type = ceres::LINE_SEARCH;\n    // options.line_search_direction_type = ceres::NONLINEAR_CONJUGATE_GRADIENT;\n    Solver::Summary summary;\n    ceres::Solve(options, &problem, &summary);\n    // std::cout << summary.FullReport() << \"\\n\";\n    std::cout << summary.BriefReport() << \"\\n\";\n\n\n    //\n    // Step-5 : Retrive Solution\n    Matrix4d T_cap;\n    PoseManipUtils::raw_to_eigenmat( T_cap_q, T_cap_t, T_cap );\n    cout << \"CERES Solution : \" << PoseManipUtils::prettyprintMatrix4d( T_cap ) << endl;\n    // cout << \"CERES Solution T_cap_t: \" << T_cap_t[0] << \" \" << T_cap_t[1] << \" \" << T_cap_t[2] << endl;\n    T_cap.col(3).topRows(3) *= n_norm;\n\n    optimized_right_T_left = T_cap;\n    cout << \"CERES Solution T_cap(after rescaling): \" << PoseManipUtils::prettyprintMatrix4d( T_cap ) << endl;\n\n\n\n}\n\n// Stereo\nint stereo_demo() {\n    IOFormat numpyFmt(FullPrecision, 0, \", \", \",\\n\", \"[\", \"]\", \"[\", \"]\");\n    ElapsedTime timer;\n\n    const std::string BASE = \"/Bulk_Data/_tmp_cerebro/mynt_multi_loops_in_lab/\";\n    // const std::string BASE = \"/Bulk_Data/ros_bags/bluefox_stereo/calib/leveled_cam_sampled/\";\n\n\n    // Abstract Camera\n    camodocal::CameraPtr left_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(BASE+\"/cameraIntrinsic.0.yaml\");\n    camodocal::CameraPtr right_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(BASE+\"/cameraIntrinsic.1.yaml\");\n    // camodocal::CameraPtr left_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(string(BASE+\"../leveled_cam_pinhole/\")+\"/camera_left.yaml\");\n    // camodocal::CameraPtr right_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(string(BASE+\"../leveled_cam_pinhole/\")+\"/camera_right.yaml\");\n\n    cout << left_camera->parametersToString() << endl;\n    cout << right_camera->parametersToString() << endl;\n\n\n    // Extrinsics - quat is wxyz . translation in meters.\n        // mynt eye\n    Vector4d q_wxyz = Vector4d( -1.8252509868889259e-04,-1.6291774489779708e-03,-1.2462127842978489e-03,9.9999787970731446e-01 );\n    Vector3d tr_xyz = Vector3d( -1.2075905420832895e+02/1000.,5.4110610639412482e-01/1000.,2.4484815673909591e-01/1000. );\n\n        // bluefox stereo leveled\n    // Vector4d q_wxyz = Vector4d( -1.7809713490350254e-03, 4.2143149583451564e-04,4.1936662160154632e-02, 9.9911859501433165e-01 );\n    // Vector3d tr_xyz = Vector3d( -1.4031938291177164e+02/1000.,-6.6214729932530441e+00/1000.,1.4808567571722902e+00/1000. );\n\n    Matrix4d right_T_left;\n    PoseManipUtils::raw_xyzw_to_eigenmat( q_wxyz, tr_xyz, right_T_left );\n\n    #if 0\n    cout << \"right_T_left(before applying delta): \" << PoseManipUtils::prettyprintMatrix4d( right_T_left ) << endl;\n    Matrix4d delta;\n    PoseManipUtils::rawyprt_to_eigenmat( Vector3d(4.0,4.0,4.0), Vector3d(0,0,0), delta );\n    right_T_left = delta * right_T_left;\n    cout << \"right_T_left(after applying delta): \" << PoseManipUtils::prettyprintMatrix4d( right_T_left ) << endl;\n    #endif\n\n    cout << \"right_T_left: \" << PoseManipUtils::prettyprintMatrix4d( right_T_left ) << endl;\n    cout << \"right_T_left=\" << right_T_left.format( numpyFmt );\n\n\n    // StereoGeometry stereogeom( left_camera,right_camera,      right_T_left  );\n    std::shared_ptr<StereoGeometry> stereogeom;\n    stereogeom = std::make_shared<StereoGeometry>( left_camera,right_camera,     right_T_left  );\n    stereogeom->set_K( 375.0, 375.0, 376.0, 240.0 );\n\n    // Raw Image - Image from camera\n    int frame_id = 1005;\n    // for( int frame_id=100 ; frame_id < 1000 ; frame_id++ )\n    {\n    cv::Mat imleft_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_id)+\".jpg\", 0 );\n    cv::Mat imright_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_id)+\"_1.jpg\", 0 );\n    // cv::Mat imleft_raw =  cv::imread( BASE+\"/cam0_\"+std::to_string(frame_id)+\".png\",0 );\n    // cv::Mat imright_raw = cv::imread( BASE+\"/cam1_\"+ std::to_string(frame_id)+\".png\",0 );\n\n\n    // Undistort Only\n    cv::Mat imleft_undistorted, imright_undistorted;\n    timer.tic();\n    stereogeom->do_image_undistortion( imleft_raw, imright_raw, imleft_undistorted, imright_undistorted );\n    cout << timer.toc_milli() << \" :(ms) stereogeom->do_image_undistortion()\\n\";\n    cv::imshow( \"imleft_raw\", imleft_raw );\n    cv::imshow( \"imright_raw\", imright_raw );\n    cv::imshow( \"imleft_undistorted\", imleft_undistorted );\n    cv::imshow( \"imright_undistorted\", imright_undistorted );\n\n    // #define YONGGEN_MARKERLESS_ONLINE_STEREOCALIB\n    #ifdef YONGGEN_MARKERLESS_ONLINE_STEREOCALIB\n    Matrix4d optimized_right_T_left;\n    nudge_extrinsics(imleft_undistorted, imright_undistorted, stereogeom->get_K(), right_T_left, optimized_right_T_left);\n    cout << \"optimized_right_T_left: \" << PoseManipUtils::prettyprintMatrix4d( optimized_right_T_left) << endl;\n    stereogeom->set_stereoextrinsic( optimized_right_T_left );\n    #endif\n\n\n\n\n    // Draw Epipolar Lines\n    cv::Mat dst_imleft_undistorted = imleft_undistorted.clone();\n    cv::Mat dst_imright_undistorted = imright_undistorted.clone();\n    stereogeom->draw_epipolarlines(dst_imleft_undistorted, dst_imright_undistorted);\n    cv::imshow( \"imleft_undistorted\", dst_imleft_undistorted );\n    cv::imshow( \"imright_undistorted\", dst_imright_undistorted );\n\n\n\n    // Stereo Rectify.\n    cv::Mat imleft_srectified, imright_srectified;\n    timer.tic();\n    stereogeom->do_stereo_rectification_of_undistorted_images(\n        imleft_undistorted, imright_undistorted,\n        imleft_srectified, imright_srectified );\n    cout << timer.toc_milli() << \" :(ms) stereogeom->do_stereo_rectification_of_undistorted_images\\n\";\n    cv::imshow( \"imleft_srectified\", imleft_srectified );\n    cv::imshow( \"imright_srectified\", imright_srectified );\n\n\n\n    // Draw Epipolar lines on stereo rectified images\n    cv::Mat dst_imleft_srectified = imleft_srectified.clone();\n    cv::Mat dst_imright_srectified = imright_srectified.clone();\n    stereogeom->draw_srectified_epipolarlines(  dst_imleft_srectified, dst_imright_srectified );\n    cv::imshow( \"dst_imleft_srectified\", dst_imleft_srectified );\n    cv::imshow( \"dst_imright_srectified\", dst_imright_srectified );\n\n\n\n    // Stereo Block Matching to compute disparity\n    cv::Mat disp_raw, disp8;\n    timer.tic();\n    stereogeom->do_stereoblockmatching_of_srectified_images( imleft_srectified, imright_srectified, disp_raw );\n    // stereogeom->do_stereoblockmatching_of_raw_images( imleft_raw, imright_raw, disp_raw );\n    cout << timer.toc_milli() << \" : (ms)do_stereoblockmatching_of_srectified_images done in\\n\";\n    cout << \"disp_raw info \" << MiscUtils::cvmat_info( disp_raw ) << endl;\n    stereogeom->print_blockmatcher_algo_info();\n    cv::normalize(disp_raw, disp8, 0, 255, CV_MINMAX, CV_8U); //< disp8 used just for visualization\n    cv::imshow( \"disp8\", disp8 );\n\n\n    #if 0\n    {\n        cv::FileStorage file(\"disp_raw.opencv.txt\", cv::FileStorage::WRITE);\n        file << \"disp_raw\" << disp_raw;\n        file.release();\n\n\n        // save disp_raw\n        MatrixXd e_disp_raw;\n        cv::cv2eigen(disp_raw, e_disp_raw );\n        RawFileIO::write_EigenMatrix(  \"./disp_raw.txt\", e_disp_raw );\n\n    }\n    #endif\n\n\n    // Disparity to pointcloud\n    const cv::Mat Q = stereogeom->get_Q();\n    cv::Mat _3dImage;\n    MatrixXd _3dpts;\n    timer.tic();\n    stereogeom->disparity_to_3DPoints( disp_raw, _3dImage, _3dpts, true, true );\n    cout << timer.toc_milli() << \" : (ms) disparity_to_3DPoints computed in \\n\";\n\n    #if 0\n    // split channels\n    cout << \"_3dImage : \" << MiscUtils::cvmat_info( _3dImage ) << endl;\n    Mat _3dImage_XYZ[3];   //destination array\n    cv::split(_3dImage,_3dImage_XYZ);//split source\n    cout << \"_3dImageX : \" << MiscUtils::cvmat_info( _3dImage_XYZ[0] ) << endl;\n    cout << \"_3dImageY : \" << MiscUtils::cvmat_info( _3dImage_XYZ[1] ) << endl;\n    cout << \"_3dImageZ : \" << MiscUtils::cvmat_info( _3dImage_XYZ[2] ) << endl;\n\n    MatrixXf e_3dImage[3];\n    cv::cv2eigen( _3dImage_XYZ[0], e_3dImage[0] );\n    cv::cv2eigen( _3dImage_XYZ[1], e_3dImage[1] );\n    cv::cv2eigen( _3dImage_XYZ[2], e_3dImage[2] );\n    RawFileIO::write_EigenMatrix(  \"./e_3dImageX.txt\", e_3dImage[0] );\n    RawFileIO::write_EigenMatrix(  \"./e_3dImageY.txt\", e_3dImage[1] );\n    RawFileIO::write_EigenMatrix(  \"./e_3dImageZ.txt\", e_3dImage[2] );\n    RawFileIO::write_EigenMatrix(  \"./_3dpts.txt\", _3dpts );\n    #endif\n\n\n\n    // Visualize 3d point cloud from stereo\n    // a) as reprojections\n    vector<cv::Scalar> pt_colors;\n    GeometryUtils::depthColors( _3dpts, pt_colors, .5, 4.5 );\n\n\n    MatrixXd perspective_proj = MatrixXd::Zero( 3, _3dpts.cols() );\n    for( int k=0 ; k<_3dpts.cols() ; k++ ) {\n        perspective_proj(0,k) = _3dpts( 0, k ) / _3dpts( 2, k) ;\n        perspective_proj(1,k) = _3dpts( 1, k ) / _3dpts( 2, k) ;\n        perspective_proj(2,k) = 1.0;\n    }\n\n    MatrixXd reproj_uv = stereogeom->get_K() * perspective_proj;\n    #if 0\n    cout << \"_3dpts(sample)\\n\" << _3dpts.leftCols(10) << endl;\n    cout << \"perspective_proj(sample)\\n\" << perspective_proj.leftCols(10) << endl;\n    cout << \"reproj_uv(sample)\\n\" << reproj_uv.leftCols(10) << endl;\n    #endif\n\n    cv::Mat dst_reproj_uv;\n    MiscUtils::plot_point_sets( imleft_srectified, reproj_uv, dst_reproj_uv, pt_colors, 0.6, \"colored by depth\" );\n    cv::imshow( \"dst_reproj_uv\", dst_reproj_uv );\n    cv::waitKey(0);\n    }\n\n}\n\nint stereo_demo_easy()\n{\n    ElapsedTime timer;\n    // const std::string BASE = \"/Bulk_Data/_tmp_cerebro/mynt_multi_loops_in_lab/\";\n    // const std::string BASE = \"/Bulk_Data/ros_bags/bluefox_stereo/calib/leveled_cam_sampled/\";\n    const std::string BASE = \"/Bulk_Data/_tmp_cerebro/mynt_seng3/\";\n\n    //--------- Intrinsics load\n    camodocal::CameraPtr left_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(BASE+\"/cameraIntrinsic.0.yaml\");\n    camodocal::CameraPtr right_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(BASE+\"/cameraIntrinsic.1.yaml\");\n    // camodocal::CameraPtr left_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(string(BASE+\"../leveled_cam_pinhole/\")+\"/camera_left.yaml\");\n    // camodocal::CameraPtr right_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(string(BASE+\"../leveled_cam_pinhole/\")+\"/camera_right.yaml\");\n\n    cout << left_camera->parametersToString() << endl;\n    cout << right_camera->parametersToString() << endl;\n\n\n    //----------- Stereo Base line load (alsoed called extrinsic calibration)\n        // mynt eye\n    Vector4d q_wxyz = Vector4d( -1.8252509868889259e-04,-1.6291774489779708e-03,-1.2462127842978489e-03,9.9999787970731446e-01 );\n    Vector3d tr_xyz = Vector3d( -1.2075905420832895e+02/1000.,5.4110610639412482e-01/1000.,2.4484815673909591e-01/1000. );\n\n        // bluefox stereo leveled\n    // Vector4d q_wxyz = Vector4d( -1.7809713490350254e-03, 4.2143149583451564e-04,4.1936662160154632e-02, 9.9911859501433165e-01 );\n    // Vector3d tr_xyz = Vector3d( -1.4031938291177164e+02/1000.,-6.6214729932530441e+00/1000.,1.4808567571722902e+00/1000. );\n\n    Matrix4d right_T_left;\n    PoseManipUtils::raw_xyzw_to_eigenmat( q_wxyz, tr_xyz, right_T_left );\n    // cout << \"right_T_left: \" << PoseManipUtils::prettyprintMatrix4d( right_T_left ) << endl;\n\n\n\n    //-------------------- init stereogeom\n    std::shared_ptr<StereoGeometry> stereogeom;\n    stereogeom = std::make_shared<StereoGeometry>( left_camera,right_camera,     right_T_left  );\n    stereogeom->set_K( 375.0, 375.0, 376.0, 240.0 );\n\n\n\n    int frame_id = 1005;\n    //----------------- load images_raw for left and right\n    for( frame_id=0; frame_id < 2500 ;  frame_id++ )\n    {\n    cout << \"READ IMAGE \" << frame_id << endl;\n    cv::Mat imleft_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_id)+\".jpg\", 0 );\n    cv::Mat imright_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_id)+\"_1.jpg\", 0 );\n    // cv::Mat imleft_raw =  cv::imread( BASE+\"/cam0_\"+std::to_string(frame_id)+\".png\",0 );\n    // cv::Mat imright_raw = cv::imread( BASE+\"/cam1_\"+ std::to_string(frame_id)+\".png\",0 );\n\n    if( imleft_raw.empty() || imright_raw.empty() )\n        continue;\n\n    //------------------- stereogeom->get3dpoints_from_raw_images()\n    //      can use one of the options depending on the need.\n    #if 0\n    // (A) fastest - if you are just looking for valid 3d points - look at the CameraGeometry.h header to see various options to call.\n    timer.tic();\n    MatrixXd _3dpts; //4xN\n    stereogeom->get3dpoints_from_raw_images(imleft_raw, imright_raw, _3dpts );\n    cout << timer.toc_milli() << \" (ms)!!  get3dpoints_from_raw_images\\n\";\n\n    cout << \"_3dpts.shape= \" << _3dpts.rows() << \" \" << _3dpts.cols() << endl;\n    #endif\n\n\n    #if 0\n    // will get the 3d points and disparity. Takes about 2-4ms more than (A)\n    MatrixXd _3dpts; //4xN\n    cv::Mat disparity_for_visualization;\n    cout << \"_3dpts.shape= \" << _3dpts.rows() << \" \" << _3dpts.cols() << endl;\n    timer.tic();\n    stereogeom->get3dpoints_and_disparity_from_raw_images(imleft_raw, imright_raw, _3dpts, disparity_for_visualization );\n    cout << timer.toc_milli() << \" (ms)!!  get3dpoints_and_disparity_from_raw_images\\n\";\n\n    cv::imshow( \"disparity_for_visualization\", disparity_for_visualization );\n    #endif\n\n\n    #if 1\n    // will get 3d points, stereo-rectified image, and disparity false colormap\n    MatrixXd _3dpts; //4xN\n    cv::Mat imleft_srectified, imright_srectified;\n    cv::Mat disparity_for_visualization;\n\n    timer.tic();\n    stereogeom->get_srectifiedim_and_3dpoints_and_disparity_from_raw_images(imleft_raw, imright_raw,\n        imleft_srectified, imright_srectified,\n         _3dpts, disparity_for_visualization );\n    cout << timer.toc_milli() << \" (ms)!!  get_srectifiedim_and_3dpoints_and_disparity_from_raw_images\\n\";\n\n    cv::imshow( \"imleft_srectified\", imleft_srectified );\n    cv::imshow( \"imright_srectified\", imright_srectified );\n    cv::imshow( \"disparity_for_visualization\", disparity_for_visualization );\n    #endif\n\n\n    //-------------------- reproject the 3d points.\n    //      note: that these 3d points after reprojections will be correct as plotted to imleft_srectified\n    #if 0\n    vector<cv::Scalar> pt_colors;\n    GeometryUtils::depthColors( _3dpts, pt_colors, .5, 4.5 );\n\n\n    MatrixXd perspective_proj = MatrixXd::Zero( 3, _3dpts.cols() ); // perspective project _3dpts.\n    for( int k=0 ; k<_3dpts.cols() ; k++ ) {\n        perspective_proj(0,k) = _3dpts( 0, k ) / _3dpts( 2, k) ;\n        perspective_proj(1,k) = _3dpts( 1, k ) / _3dpts( 2, k) ;\n        perspective_proj(2,k) = 1.0;\n    }\n\n    MatrixXd reproj_uv = stereogeom->get_K() * perspective_proj; //< scaling with camera intrinsic for visualization\n    cout << \"_3dpts(sample)\\n\" << _3dpts.leftCols(10) << endl;\n    cout << \"perspective_proj(sample)\\n\" << perspective_proj.leftCols(10) << endl;\n    cout << \"reproj_uv(sample)\\n\" << reproj_uv.leftCols(10) << endl;\n\n\n    cv::Mat dst_reproj_uv;\n    MiscUtils::plot_point_sets( imleft_srectified, reproj_uv, dst_reproj_uv, pt_colors, 0.6, \"plot of reprojected points;colored by depth\" );\n    cv::imshow( \"dst_reproj_uv\", dst_reproj_uv );\n    #endif\n\n    //-------------------- visualize 3d points with rviz\n\n    cv::waitKey(0);\n    }\n\n\n\n}\n\n// Monocular\nint monocular_demo()\n{\n    const std::string BASE = \"/Bulk_Data/_tmp_cerebro/mynt_multi_loops_in_lab/\";\n\n\n    // Abstract Camera\n    camodocal::CameraPtr m_camera = camodocal::CameraFactory::instance()->generateCameraFromYamlFile(BASE+\"/cameraIntrinsic.0.yaml\");\n    cout << m_camera->parametersToString() << endl;\n\n    // Init Monocular Geometry\n    MonoGeometry monogeom( m_camera );\n\n    // Raw Image - Image from camera\n    int frame_id = 23;\n    cv::Mat im_raw =  cv::imread( BASE+\"/\"+std::to_string(frame_id)+\".jpg\" );\n\n    cv::Mat im_undistorted;\n    monogeom.do_image_undistortion( im_raw, im_undistorted );\n\n\n    cv::imshow( \"im_raw\", im_raw );\n    cv::imshow( \"im_undistorted\", im_undistorted );\n    cv::waitKey(0);\n}\n\nint main() {\n    // monocular_demo();\n    // stereo_demo();\n    stereo_demo_easy();\n\n    /*\n    double _x[5];\n    _x[0] = 0.545435;\n    _x[1] = .8;\n    _x[2] = .25;\n\n    double _m[5], _n[5];\n    UnitVectorParameterization parm = UnitVectorParameterization();\n    parm.gram_schmidt_orthonormalization( _x, _m, _n );\n    */\n}\n"
  },
  {
    "path": "src/utils/camodocal/README.md",
    "content": "# Camodocal\n\nOriginal Implementation : https://github.com/hengli/camodocal\nMy (Manohar Kuse, mpkuse@connect.ust.hk) Adaptation : https://github.com/mpkuse/camera_model\n\n## CMakefile.txt\nIf you put this folder under `src/utils/` and CMakeLists.txt is in `./`\nyou will need to add the following to you CMake files\n\n```\ninclude_directories(src/utils/camodocal/include)\nFILE(GLOB CamodocalCameraModelSources\n        src/utils/camodocal/src/chessboard/Chessboard.cc\n        src/utils/camodocal/src/calib/CameraCalibration.cc\n        src/utils/camodocal/src/camera_models/Camera.cc\n        src/utils/camodocal/src/camera_models/CameraFactory.cc\n        src/utils/camodocal/src/camera_models/CostFunctionFactory.cc\n        src/utils/camodocal/src/camera_models/PinholeCamera.cc\n        src/utils/camodocal/src/camera_models/CataCamera.cc\n        src/utils/camodocal/src/camera_models/EquidistantCamera.cc\n        src/utils/camodocal/src/camera_models/ScaramuzzaCamera.cc\n        src/utils/camodocal/src/sparse_graph/Transform.cc\n        src/utils/camodocal/src/gpl/gpl.cc\n        src/utils/camodocal/src/gpl/EigenQuaternionParameterization.cc\n    )\n```\n\nDependencies if not already added:\n```\nfind_package(Eigen3 REQUIRED)\nfind_package(Ceres REQUIRED)\nfind_package(OpenCV 3 REQUIRED)\nfind_package(Boost REQUIRED COMPONENTS filesystem program_options system)\n\n```\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/calib/CameraCalibration.h",
    "content": "#ifndef CAMERACALIBRATION_H\n#define CAMERACALIBRATION_H\n\n#include <opencv2/core/core.hpp>\n\n#include \"camodocal/camera_models/Camera.h\"\n\nnamespace camodocal\n{\n\nclass CameraCalibration\n{\npublic:\n    EIGEN_MAKE_ALIGNED_OPERATOR_NEW\n    CameraCalibration();\n\n    CameraCalibration(Camera::ModelType modelType,\n                      const std::string& cameraName,\n                      const cv::Size& imageSize,\n                      const cv::Size& boardSize,\n                      float squareSize);\n\n    void clear(void);\n\n    void addChessboardData(const std::vector<cv::Point2f>& corners);\n\n    bool calibrate(void);\n\n    int sampleCount(void) const;\n    std::vector<std::vector<cv::Point2f> >& imagePoints(void);\n    const std::vector<std::vector<cv::Point2f> >& imagePoints(void) const;\n    std::vector<std::vector<cv::Point3f> >& scenePoints(void);\n    const std::vector<std::vector<cv::Point3f> >& scenePoints(void) const;\n    CameraPtr& camera(void);\n    const CameraConstPtr camera(void) const;\n\n    Eigen::Matrix2d& measurementCovariance(void);\n    const Eigen::Matrix2d& measurementCovariance(void) const;\n\n    cv::Mat& cameraPoses(void);\n    const cv::Mat& cameraPoses(void) const;\n\n    void drawResults(std::vector<cv::Mat>& images) const;\n\n    void writeParams(const std::string& filename) const;\n\n    bool writeChessboardData(const std::string& filename) const;\n    bool readChessboardData(const std::string& filename);\n\n    void setVerbose(bool verbose);\n\nprivate:\n    bool calibrateHelper(CameraPtr& camera,\n                         std::vector<cv::Mat>& rvecs, std::vector<cv::Mat>& tvecs) const;\n\n    void optimize(CameraPtr& camera,\n                  std::vector<cv::Mat>& rvecs, std::vector<cv::Mat>& tvecs) const;\n\n    template<typename T>\n    void readData(std::ifstream& ifs, T& data) const;\n\n    template<typename T>\n    void writeData(std::ofstream& ofs, T data) const;\n\n    cv::Size m_boardSize;\n    float m_squareSize;\n\n    CameraPtr m_camera;\n    cv::Mat m_cameraPoses;\n\n    std::vector<std::vector<cv::Point2f> > m_imagePoints;\n    std::vector<std::vector<cv::Point3f> > m_scenePoints;\n\n    Eigen::Matrix2d m_measurementCovariance;\n\n    bool m_verbose;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/camera_models/Camera.h",
    "content": "#ifndef CAMERA_H\n#define CAMERA_H\n\n#include <boost/shared_ptr.hpp>\n#include <eigen3/Eigen/Dense>\n#include <opencv2/core/core.hpp>\n#include <vector>\n\nnamespace camodocal\n{\n\nclass Camera\n{\npublic:\n    EIGEN_MAKE_ALIGNED_OPERATOR_NEW\n    enum ModelType\n    {\n        KANNALA_BRANDT,\n        MEI,\n        PINHOLE,\n        SCARAMUZZA\n    };\n\n    class Parameters\n    {\n    public:\n        EIGEN_MAKE_ALIGNED_OPERATOR_NEW\n        Parameters(ModelType modelType);\n\n        Parameters(ModelType modelType, const std::string& cameraName,\n                   int w, int h);\n\n        ModelType& modelType(void);\n        std::string& cameraName(void);\n        int& imageWidth(void);\n        int& imageHeight(void);\n\n        ModelType modelType(void) const;\n        const std::string& cameraName(void) const;\n        int imageWidth(void) const;\n        int imageHeight(void) const;\n\n        int nIntrinsics(void) const;\n\n        virtual bool readFromYamlFile(const std::string& filename) = 0;\n        virtual void writeToYamlFile(const std::string& filename) const = 0;\n\n    protected:\n        ModelType m_modelType;\n        int m_nIntrinsics;\n        std::string m_cameraName;\n        int m_imageWidth;\n        int m_imageHeight;\n    };\n\n    virtual ModelType modelType(void) const = 0;\n    virtual const std::string& cameraName(void) const = 0;\n    virtual int imageWidth(void) const = 0;\n    virtual int imageHeight(void) const = 0;\n\n    virtual cv::Mat& mask(void);\n    virtual const cv::Mat& mask(void) const;\n\n    virtual void estimateIntrinsics(const cv::Size& boardSize,\n                                    const std::vector< std::vector<cv::Point3f> >& objectPoints,\n                                    const std::vector< std::vector<cv::Point2f> >& imagePoints) = 0;\n    virtual void estimateExtrinsics(const std::vector<cv::Point3f>& objectPoints,\n                                    const std::vector<cv::Point2f>& imagePoints,\n                                    cv::Mat& rvec, cv::Mat& tvec) const;\n\n    // Lift points from the image plane to the sphere\n    virtual void liftSphere(const Eigen::Vector2d& p, Eigen::Vector3d& P) const = 0;\n    //%output P\n\n    // Lift points from the image plane to the projective space\n    virtual void liftProjective(const Eigen::Vector2d& p, Eigen::Vector3d& P) const = 0;\n    //%output P\n\n    // Projects 3D points to the image plane (Pi function)\n    virtual void spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p) const = 0;\n    //%output p\n\n    // Projects 3D points to the image plane (Pi function)\n    // and calculates jacobian\n    //virtual void spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p,\n    //                          Eigen::Matrix<double,2,3>& J) const = 0;\n    //%output p\n    //%output J\n\n    virtual void undistToPlane(const Eigen::Vector2d& p_u, Eigen::Vector2d& p) const = 0;\n    //%output p\n\n    //virtual void initUndistortMap(cv::Mat& map1, cv::Mat& map2, double fScale = 1.0) const = 0;\n    virtual cv::Mat initUndistortRectifyMap(cv::Mat& map1, cv::Mat& map2,\n                                            float fx = -1.0f, float fy = -1.0f,\n                                            cv::Size imageSize = cv::Size(0, 0),\n                                            float cx = -1.0f, float cy = -1.0f,\n                                            cv::Mat rmat = cv::Mat::eye(3, 3, CV_32F)) const = 0;\n\n    virtual int parameterCount(void) const = 0;\n\n    virtual void readParameters(const std::vector<double>& parameters) = 0;\n    virtual void writeParameters(std::vector<double>& parameters) const = 0;\n\n    virtual void writeParametersToYamlFile(const std::string& filename) const = 0;\n\n    virtual std::string parametersToString(void) const = 0;\n\n    /**\n     * \\brief Calculates the reprojection distance between points\n     *\n     * \\param P1 first 3D point coordinates\n     * \\param P2 second 3D point coordinates\n     * \\return euclidean distance in the plane\n     */\n    double reprojectionDist(const Eigen::Vector3d& P1, const Eigen::Vector3d& P2) const;\n\n    double reprojectionError(const std::vector< std::vector<cv::Point3f> >& objectPoints,\n                             const std::vector< std::vector<cv::Point2f> >& imagePoints,\n                             const std::vector<cv::Mat>& rvecs,\n                             const std::vector<cv::Mat>& tvecs,\n                             cv::OutputArray perViewErrors = cv::noArray()) const;\n\n    double reprojectionError(const Eigen::Vector3d& P,\n                             const Eigen::Quaterniond& camera_q,\n                             const Eigen::Vector3d& camera_t,\n                             const Eigen::Vector2d& observed_p) const;\n\n    void projectPoints(const std::vector<cv::Point3f>& objectPoints,\n                       const cv::Mat& rvec,\n                       const cv::Mat& tvec,\n                       std::vector<cv::Point2f>& imagePoints) const;\nprotected:\n    cv::Mat m_mask;\n};\n\ntypedef boost::shared_ptr<Camera> CameraPtr;\ntypedef boost::shared_ptr<const Camera> CameraConstPtr;\n\n}\n\n#endif\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/camera_models/CameraFactory.h",
    "content": "#ifndef CAMERAFACTORY_H\n#define CAMERAFACTORY_H\n\n#include <boost/shared_ptr.hpp>\n#include <opencv2/core/core.hpp>\n\n#include \"camodocal/camera_models/Camera.h\"\n\nnamespace camodocal\n{\n\nclass CameraFactory\n{\npublic:\n    EIGEN_MAKE_ALIGNED_OPERATOR_NEW\n    CameraFactory();\n\n    static boost::shared_ptr<CameraFactory> instance(void);\n\n    CameraPtr generateCamera(Camera::ModelType modelType,\n                             const std::string& cameraName,\n                             cv::Size imageSize) const;\n\n    CameraPtr generateCameraFromYamlFile(const std::string& filename);\n\nprivate:\n    static boost::shared_ptr<CameraFactory> m_instance;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/camera_models/CataCamera.h",
    "content": "#ifndef CATACAMERA_H\r\n#define CATACAMERA_H\r\n\r\n#include <opencv2/core/core.hpp>\r\n#include <string>\r\n\r\n#include \"ceres/rotation.h\"\r\n#include \"Camera.h\"\r\n\r\nnamespace camodocal\r\n{\r\n\r\n/**\r\n * C. Mei, and P. Rives, Single View Point Omnidirectional Camera Calibration\r\n * from Planar Grids, ICRA 2007\r\n */\r\n\r\nclass CataCamera: public Camera\r\n{\r\npublic:\r\n    class Parameters: public Camera::Parameters\r\n    {\r\n    public:\r\n        Parameters();\r\n        Parameters(const std::string& cameraName,\r\n                   int w, int h,\r\n                   double xi,\r\n                   double k1, double k2, double p1, double p2,\r\n                   double gamma1, double gamma2, double u0, double v0);\r\n\r\n        double& xi(void);\r\n        double& k1(void);\r\n        double& k2(void);\r\n        double& p1(void);\r\n        double& p2(void);\r\n        double& gamma1(void);\r\n        double& gamma2(void);\r\n        double& u0(void);\r\n        double& v0(void);\r\n\r\n        double xi(void) const;\r\n        double k1(void) const;\r\n        double k2(void) const;\r\n        double p1(void) const;\r\n        double p2(void) const;\r\n        double gamma1(void) const;\r\n        double gamma2(void) const;\r\n        double u0(void) const;\r\n        double v0(void) const;\r\n\r\n        bool readFromYamlFile(const std::string& filename);\r\n        void writeToYamlFile(const std::string& filename) const;\r\n\r\n        Parameters& operator=(const Parameters& other);\r\n        friend std::ostream& operator<< (std::ostream& out, const Parameters& params);\r\n\r\n    private:\r\n        double m_xi;\r\n        double m_k1;\r\n        double m_k2;\r\n        double m_p1;\r\n        double m_p2;\r\n        double m_gamma1;\r\n        double m_gamma2;\r\n        double m_u0;\r\n        double m_v0;\r\n    };\r\n\r\n    CataCamera();\r\n\r\n    /**\r\n    * \\brief Constructor from the projection model parameters\r\n    */\r\n    CataCamera(const std::string& cameraName,\r\n               int imageWidth, int imageHeight,\r\n               double xi, double k1, double k2, double p1, double p2,\r\n               double gamma1, double gamma2, double u0, double v0);\r\n    /**\r\n    * \\brief Constructor from the projection model parameters\r\n    */\r\n    CataCamera(const Parameters& params);\r\n\r\n    Camera::ModelType modelType(void) const;\r\n    const std::string& cameraName(void) const;\r\n    int imageWidth(void) const;\r\n    int imageHeight(void) const;\r\n\r\n    void estimateIntrinsics(const cv::Size& boardSize,\r\n                            const std::vector< std::vector<cv::Point3f> >& objectPoints,\r\n                            const std::vector< std::vector<cv::Point2f> >& imagePoints);\r\n\r\n    // Lift points from the image plane to the sphere\r\n    void liftSphere(const Eigen::Vector2d& p, Eigen::Vector3d& P) const;\r\n    //%output P\r\n\r\n    // Lift points from the image plane to the projective space\r\n    void liftProjective(const Eigen::Vector2d& p, Eigen::Vector3d& P) const;\r\n    //%output P\r\n\r\n    // Projects 3D points to the image plane (Pi function)\r\n    void spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p) const;\r\n    //%output p\r\n\r\n    // Projects 3D points to the image plane (Pi function)\r\n    // and calculates jacobian\r\n    void spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p,\r\n                      Eigen::Matrix<double,2,3>& J) const;\r\n    //%output p\r\n    //%output J\r\n\r\n    void undistToPlane(const Eigen::Vector2d& p_u, Eigen::Vector2d& p) const;\r\n    //%output p\r\n\r\n    template <typename T>\r\n    static void spaceToPlane(const T* const params,\r\n                             const T* const q, const T* const t,\r\n                             const Eigen::Matrix<T, 3, 1>& P,\r\n                             Eigen::Matrix<T, 2, 1>& p);\r\n\r\n    void distortion(const Eigen::Vector2d& p_u, Eigen::Vector2d& d_u) const;\r\n    void distortion(const Eigen::Vector2d& p_u, Eigen::Vector2d& d_u,\r\n                    Eigen::Matrix2d& J) const;\r\n\r\n    void initUndistortMap(cv::Mat& map1, cv::Mat& map2, double fScale = 1.0) const;\r\n    cv::Mat initUndistortRectifyMap(cv::Mat& map1, cv::Mat& map2,\r\n                                    float fx = -1.0f, float fy = -1.0f,\r\n                                    cv::Size imageSize = cv::Size(0, 0),\r\n                                    float cx = -1.0f, float cy = -1.0f,\r\n                                    cv::Mat rmat = cv::Mat::eye(3, 3, CV_32F)) const;\r\n\r\n    int parameterCount(void) const;\r\n\r\n    const Parameters& getParameters(void) const;\r\n    void setParameters(const Parameters& parameters);\r\n\r\n    void readParameters(const std::vector<double>& parameterVec);\r\n    void writeParameters(std::vector<double>& parameterVec) const;\r\n\r\n    void writeParametersToYamlFile(const std::string& filename) const;\r\n\r\n    std::string parametersToString(void) const;\r\n\r\nprivate:\r\n    Parameters mParameters;\r\n\r\n    double m_inv_K11, m_inv_K13, m_inv_K22, m_inv_K23;\r\n    bool m_noDistortion;\r\n};\r\n\r\ntypedef boost::shared_ptr<CataCamera> CataCameraPtr;\r\ntypedef boost::shared_ptr<const CataCamera> CataCameraConstPtr;\r\n\r\ntemplate <typename T>\r\nvoid\r\nCataCamera::spaceToPlane(const T* const params,\r\n                         const T* const q, const T* const t,\r\n                         const Eigen::Matrix<T, 3, 1>& P,\r\n                         Eigen::Matrix<T, 2, 1>& p)\r\n{\r\n    T P_w[3];\r\n    P_w[0] = T(P(0));\r\n    P_w[1] = T(P(1));\r\n    P_w[2] = T(P(2));\r\n\r\n    // Convert quaternion from Eigen convention (x, y, z, w)\r\n    // to Ceres convention (w, x, y, z)\r\n    T q_ceres[4] = {q[3], q[0], q[1], q[2]};\r\n\r\n    T P_c[3];\r\n    ceres::QuaternionRotatePoint(q_ceres, P_w, P_c);\r\n\r\n    P_c[0] += t[0];\r\n    P_c[1] += t[1];\r\n    P_c[2] += t[2];\r\n\r\n    // project 3D object point to the image plane\r\n    T xi = params[0];\r\n    T k1 = params[1];\r\n    T k2 = params[2];\r\n    T p1 = params[3];\r\n    T p2 = params[4];\r\n    T gamma1 = params[5];\r\n    T gamma2 = params[6];\r\n    T alpha = T(0); //cameraParams.alpha();\r\n    T u0 = params[7];\r\n    T v0 = params[8];\r\n\r\n    // Transform to model plane\r\n    T len = sqrt(P_c[0] * P_c[0] + P_c[1] * P_c[1] + P_c[2] * P_c[2]);\r\n    P_c[0] /= len;\r\n    P_c[1] /= len;\r\n    P_c[2] /= len;\r\n\r\n    T u = P_c[0] / (P_c[2] + xi);\r\n    T v = P_c[1] / (P_c[2] + xi);\r\n\r\n    T rho_sqr = u * u + v * v;\r\n    T L = T(1.0) + k1 * rho_sqr + k2 * rho_sqr * rho_sqr;\r\n    T du = T(2.0) * p1 * u * v + p2 * (rho_sqr + T(2.0) * u * u);\r\n    T dv = p1 * (rho_sqr + T(2.0) * v * v) + T(2.0) * p2 * u * v;\r\n\r\n    u = L * u + du;\r\n    v = L * v + dv;\r\n    p(0) = gamma1 * (u + alpha * v) + u0;\r\n    p(1) = gamma2 * v + v0;\r\n}\r\n\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/camera_models/CostFunctionFactory.h",
    "content": "#ifndef COSTFUNCTIONFACTORY_H\n#define COSTFUNCTIONFACTORY_H\n\n#include <boost/shared_ptr.hpp>\n#include <opencv2/core/core.hpp>\n\n#include \"camodocal/camera_models/Camera.h\"\n\nnamespace ceres\n{\n    class CostFunction;\n}\n\nnamespace camodocal\n{\n\nenum\n{\n    CAMERA_INTRINSICS =         1 << 0,\n    CAMERA_POSE =               1 << 1,\n    POINT_3D =                  1 << 2,\n    ODOMETRY_INTRINSICS =       1 << 3,\n    ODOMETRY_3D_POSE =          1 << 4,\n    ODOMETRY_6D_POSE =          1 << 5,\n    CAMERA_ODOMETRY_TRANSFORM = 1 << 6\n};\n\nclass CostFunctionFactory\n{\npublic:\n    EIGEN_MAKE_ALIGNED_OPERATOR_NEW\n    CostFunctionFactory();\n\n    static boost::shared_ptr<CostFunctionFactory> instance(void);\n\n    ceres::CostFunction* generateCostFunction(const CameraConstPtr& camera,\n                                              const Eigen::Vector3d& observed_P,\n                                              const Eigen::Vector2d& observed_p,\n                                              int flags) const;\n\n    ceres::CostFunction* generateCostFunction(const CameraConstPtr& camera,\n                                              const Eigen::Vector3d& observed_P,\n                                              const Eigen::Vector2d& observed_p,\n                                              const Eigen::Matrix2d& sqrtPrecisionMat,\n                                              int flags) const;\n\n    ceres::CostFunction* generateCostFunction(const CameraConstPtr& camera,\n                                              const Eigen::Vector2d& observed_p,\n                                              int flags, bool optimize_cam_odo_z = true) const;\n\n    ceres::CostFunction* generateCostFunction(const CameraConstPtr& camera,\n                                              const Eigen::Vector2d& observed_p,\n                                              const Eigen::Matrix2d& sqrtPrecisionMat,\n                                              int flags, bool optimize_cam_odo_z = true) const;\n\n    ceres::CostFunction* generateCostFunction(const CameraConstPtr& camera,\n                                              const Eigen::Vector3d& odo_pos,\n                                              const Eigen::Vector3d& odo_att,\n                                              const Eigen::Vector2d& observed_p,\n                                              int flags, bool optimize_cam_odo_z = true) const;\n\n    ceres::CostFunction* generateCostFunction(const CameraConstPtr& camera,\n                                              const Eigen::Quaterniond& cam_odo_q,\n                                              const Eigen::Vector3d& cam_odo_t,\n                                              const Eigen::Vector3d& odo_pos,\n                                              const Eigen::Vector3d& odo_att,\n                                              const Eigen::Vector2d& observed_p,\n                                              int flags) const;\n\n    ceres::CostFunction* generateCostFunction(const CameraConstPtr& cameraLeft,\n                                              const CameraConstPtr& cameraRight,\n                                              const Eigen::Vector3d& observed_P,\n                                              const Eigen::Vector2d& observed_p_left,\n                                              const Eigen::Vector2d& observed_p_right) const;\n\nprivate:\n    static boost::shared_ptr<CostFunctionFactory> m_instance;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/camera_models/EquidistantCamera.h",
    "content": "#ifndef EQUIDISTANTCAMERA_H\r\n#define EQUIDISTANTCAMERA_H\r\n\r\n#include <opencv2/core/core.hpp>\r\n#include <string>\r\n\r\n#include \"ceres/rotation.h\"\r\n#include \"Camera.h\"\r\n\r\nnamespace camodocal\r\n{\r\n\r\n/**\r\n * J. Kannala, and S. Brandt, A Generic Camera Model and Calibration Method\r\n * for Conventional, Wide-Angle, and Fish-Eye Lenses, PAMI 2006\r\n */\r\n\r\nclass EquidistantCamera: public Camera\r\n{\r\npublic:\r\n    class Parameters: public Camera::Parameters\r\n    {\r\n    public:\r\n        Parameters();\r\n        Parameters(const std::string& cameraName,\r\n                   int w, int h,\r\n                   double k2, double k3, double k4, double k5,\r\n                   double mu, double mv,\r\n                   double u0, double v0);\r\n\r\n        double& k2(void);\r\n        double& k3(void);\r\n        double& k4(void);\r\n        double& k5(void);\r\n        double& mu(void);\r\n        double& mv(void);\r\n        double& u0(void);\r\n        double& v0(void);\r\n\r\n        double k2(void) const;\r\n        double k3(void) const;\r\n        double k4(void) const;\r\n        double k5(void) const;\r\n        double mu(void) const;\r\n        double mv(void) const;\r\n        double u0(void) const;\r\n        double v0(void) const;\r\n\r\n        bool readFromYamlFile(const std::string& filename);\r\n        void writeToYamlFile(const std::string& filename) const;\r\n\r\n        Parameters& operator=(const Parameters& other);\r\n        friend std::ostream& operator<< (std::ostream& out, const Parameters& params);\r\n\r\n    private:\r\n        // projection\r\n        double m_k2;\r\n        double m_k3;\r\n        double m_k4;\r\n        double m_k5;\r\n\r\n        double m_mu;\r\n        double m_mv;\r\n        double m_u0;\r\n        double m_v0;\r\n    };\r\n\r\n    EquidistantCamera();\r\n\r\n    /**\r\n    * \\brief Constructor from the projection model parameters\r\n    */\r\n    EquidistantCamera(const std::string& cameraName,\r\n                      int imageWidth, int imageHeight,\r\n                      double k2, double k3, double k4, double k5,\r\n                      double mu, double mv,\r\n                      double u0, double v0);\r\n    /**\r\n    * \\brief Constructor from the projection model parameters\r\n    */\r\n    EquidistantCamera(const Parameters& params);\r\n\r\n    Camera::ModelType modelType(void) const;\r\n    const std::string& cameraName(void) const;\r\n    int imageWidth(void) const;\r\n    int imageHeight(void) const;\r\n\r\n    void estimateIntrinsics(const cv::Size& boardSize,\r\n                            const std::vector< std::vector<cv::Point3f> >& objectPoints,\r\n                            const std::vector< std::vector<cv::Point2f> >& imagePoints);\r\n\r\n    // Lift points from the image plane to the sphere\r\n    virtual void liftSphere(const Eigen::Vector2d& p, Eigen::Vector3d& P) const;\r\n    //%output P\r\n\r\n    // Lift points from the image plane to the projective space\r\n    void liftProjective(const Eigen::Vector2d& p, Eigen::Vector3d& P) const;\r\n    //%output P\r\n\r\n    // Projects 3D points to the image plane (Pi function)\r\n    void spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p) const;\r\n    //%output p\r\n\r\n    // Projects 3D points to the image plane (Pi function)\r\n    // and calculates jacobian\r\n    void spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p,\r\n                      Eigen::Matrix<double,2,3>& J) const;\r\n    //%output p\r\n    //%output J\r\n\r\n    void undistToPlane(const Eigen::Vector2d& p_u, Eigen::Vector2d& p) const;\r\n    //%output p\r\n\r\n    template <typename T>\r\n    static void spaceToPlane(const T* const params,\r\n                             const T* const q, const T* const t,\r\n                             const Eigen::Matrix<T, 3, 1>& P,\r\n                             Eigen::Matrix<T, 2, 1>& p);\r\n\r\n    void initUndistortMap(cv::Mat& map1, cv::Mat& map2, double fScale = 1.0) const;\r\n    cv::Mat initUndistortRectifyMap(cv::Mat& map1, cv::Mat& map2,\r\n                                    float fx = -1.0f, float fy = -1.0f,\r\n                                    cv::Size imageSize = cv::Size(0, 0),\r\n                                    float cx = -1.0f, float cy = -1.0f,\r\n                                    cv::Mat rmat = cv::Mat::eye(3, 3, CV_32F)) const;\r\n\r\n    int parameterCount(void) const;\r\n\r\n    const Parameters& getParameters(void) const;\r\n    void setParameters(const Parameters& parameters);\r\n\r\n    void readParameters(const std::vector<double>& parameterVec);\r\n    void writeParameters(std::vector<double>& parameterVec) const;\r\n\r\n    void writeParametersToYamlFile(const std::string& filename) const;\r\n\r\n    std::string parametersToString(void) const;\r\n\r\nprivate:\r\n    template<typename T>\r\n    static T r(T k2, T k3, T k4, T k5, T theta);\r\n\r\n\r\n    void fitOddPoly(const std::vector<double>& x, const std::vector<double>& y,\r\n                    int n, std::vector<double>& coeffs) const;\r\n\r\n    void backprojectSymmetric(const Eigen::Vector2d& p_u,\r\n                              double& theta, double& phi) const;\r\n\r\n    Parameters mParameters;\r\n\r\n    double m_inv_K11, m_inv_K13, m_inv_K22, m_inv_K23;\r\n};\r\n\r\ntypedef boost::shared_ptr<EquidistantCamera> EquidistantCameraPtr;\r\ntypedef boost::shared_ptr<const EquidistantCamera> EquidistantCameraConstPtr;\r\n\r\ntemplate<typename T>\r\nT\r\nEquidistantCamera::r(T k2, T k3, T k4, T k5, T theta)\r\n{\r\n    // k1 = 1\r\n    return theta +\r\n           k2 * theta * theta * theta +\r\n           k3 * theta * theta * theta * theta * theta +\r\n           k4 * theta * theta * theta * theta * theta * theta * theta +\r\n           k5 * theta * theta * theta * theta * theta * theta * theta * theta * theta;\r\n}\r\n\r\ntemplate <typename T>\r\nvoid\r\nEquidistantCamera::spaceToPlane(const T* const params,\r\n                                const T* const q, const T* const t,\r\n                                const Eigen::Matrix<T, 3, 1>& P,\r\n                                Eigen::Matrix<T, 2, 1>& p)\r\n{\r\n    T P_w[3];\r\n    P_w[0] = T(P(0));\r\n    P_w[1] = T(P(1));\r\n    P_w[2] = T(P(2));\r\n\r\n    // Convert quaternion from Eigen convention (x, y, z, w)\r\n    // to Ceres convention (w, x, y, z)\r\n    T q_ceres[4] = {q[3], q[0], q[1], q[2]};\r\n\r\n    T P_c[3];\r\n    ceres::QuaternionRotatePoint(q_ceres, P_w, P_c);\r\n\r\n    P_c[0] += t[0];\r\n    P_c[1] += t[1];\r\n    P_c[2] += t[2];\r\n\r\n    // project 3D object point to the image plane;\r\n    T k2 = params[0];\r\n    T k3 = params[1];\r\n    T k4 = params[2];\r\n    T k5 = params[3];\r\n    T mu = params[4];\r\n    T mv = params[5];\r\n    T u0 = params[6];\r\n    T v0 = params[7];\r\n\r\n    T len = sqrt(P_c[0] * P_c[0] + P_c[1] * P_c[1] + P_c[2] * P_c[2]);\r\n    T theta = acos(P_c[2] / len);\r\n    T phi = atan2(P_c[1], P_c[0]);\r\n\r\n    Eigen::Matrix<T,2,1> p_u = r(k2, k3, k4, k5, theta) * Eigen::Matrix<T,2,1>(cos(phi), sin(phi));\r\n\r\n    p(0) = mu * p_u(0) + u0;\r\n    p(1) = mv * p_u(1) + v0;\r\n}\r\n\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/camera_models/PinholeCamera.h",
    "content": "#ifndef PINHOLECAMERA_H\n#define PINHOLECAMERA_H\n\n#include <opencv2/core/core.hpp>\n#include <string>\n\n#include \"ceres/rotation.h\"\n#include \"Camera.h\"\n\nnamespace camodocal\n{\n\nclass PinholeCamera: public Camera\n{\npublic:\n    class Parameters: public Camera::Parameters\n    {\n    public:\n        Parameters();\n        Parameters(const std::string& cameraName,\n                   int w, int h,\n                   double k1, double k2, double p1, double p2,\n                   double fx, double fy, double cx, double cy);\n\n        double& k1(void);\n        double& k2(void);\n        double& p1(void);\n        double& p2(void);\n        double& fx(void);\n        double& fy(void);\n        double& cx(void);\n        double& cy(void);\n\n        double xi(void) const;\n        double k1(void) const;\n        double k2(void) const;\n        double p1(void) const;\n        double p2(void) const;\n        double fx(void) const;\n        double fy(void) const;\n        double cx(void) const;\n        double cy(void) const;\n\n        bool readFromYamlFile(const std::string& filename);\n        void writeToYamlFile(const std::string& filename) const;\n\n        Parameters& operator=(const Parameters& other);\n        friend std::ostream& operator<< (std::ostream& out, const Parameters& params);\n\n    private:\n        double m_k1;\n        double m_k2;\n        double m_p1;\n        double m_p2;\n        double m_fx;\n        double m_fy;\n        double m_cx;\n        double m_cy;\n    };\n\n    PinholeCamera();\n\n    /**\n    * \\brief Constructor from the projection model parameters\n    */\n    PinholeCamera(const std::string& cameraName,\n                  int imageWidth, int imageHeight,\n                  double k1, double k2, double p1, double p2,\n                  double fx, double fy, double cx, double cy);\n    /**\n    * \\brief Constructor from the projection model parameters\n    */\n    PinholeCamera(const Parameters& params);\n\n    Camera::ModelType modelType(void) const;\n    const std::string& cameraName(void) const;\n    int imageWidth(void) const;\n    int imageHeight(void) const;\n\n    void estimateIntrinsics(const cv::Size& boardSize,\n                            const std::vector< std::vector<cv::Point3f> >& objectPoints,\n                            const std::vector< std::vector<cv::Point2f> >& imagePoints);\n\n    // Lift points from the image plane to the sphere\n    virtual void liftSphere(const Eigen::Vector2d& p, Eigen::Vector3d& P) const;\n    //%output P\n\n    // Lift points from the image plane to the projective space\n    void liftProjective(const Eigen::Vector2d& p, Eigen::Vector3d& P) const;\n    //%output P\n\n    // Projects 3D points to the image plane (Pi function)\n    void spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p) const;\n    //%output p\n\n    // Projects 3D points to the image plane (Pi function)\n    // and calculates jacobian\n    void spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p,\n                      Eigen::Matrix<double,2,3>& J) const;\n    //%output p\n    //%output J\n\n    void undistToPlane(const Eigen::Vector2d& p_u, Eigen::Vector2d& p) const;\n    //%output p\n\n    template <typename T>\n    static void spaceToPlane(const T* const params,\n                             const T* const q, const T* const t,\n                             const Eigen::Matrix<T, 3, 1>& P,\n                             Eigen::Matrix<T, 2, 1>& p);\n\n    void distortion(const Eigen::Vector2d& p_u, Eigen::Vector2d& d_u) const;\n    void distortion(const Eigen::Vector2d& p_u, Eigen::Vector2d& d_u,\n                    Eigen::Matrix2d& J) const;\n\n    void initUndistortMap(cv::Mat& map1, cv::Mat& map2, double fScale = 1.0) const;\n    cv::Mat initUndistortRectifyMap(cv::Mat& map1, cv::Mat& map2,\n                                    float fx = -1.0f, float fy = -1.0f,\n                                    cv::Size imageSize = cv::Size(0, 0),\n                                    float cx = -1.0f, float cy = -1.0f,\n                                    cv::Mat rmat = cv::Mat::eye(3, 3, CV_32F)) const;\n\n    int parameterCount(void) const;\n\n    const Parameters& getParameters(void) const;\n    void setParameters(const Parameters& parameters);\n\n    void readParameters(const std::vector<double>& parameterVec);\n    void writeParameters(std::vector<double>& parameterVec) const;\n\n    void writeParametersToYamlFile(const std::string& filename) const;\n\n    std::string parametersToString(void) const;\n\nprivate:\n    Parameters mParameters;\n\n    double m_inv_K11, m_inv_K13, m_inv_K22, m_inv_K23;\n    bool m_noDistortion;\n};\n\ntypedef boost::shared_ptr<PinholeCamera> PinholeCameraPtr;\ntypedef boost::shared_ptr<const PinholeCamera> PinholeCameraConstPtr;\n\ntemplate <typename T>\nvoid\nPinholeCamera::spaceToPlane(const T* const params,\n                            const T* const q, const T* const t,\n                            const Eigen::Matrix<T, 3, 1>& P,\n                            Eigen::Matrix<T, 2, 1>& p)\n{\n    T P_w[3];\n    P_w[0] = T(P(0));\n    P_w[1] = T(P(1));\n    P_w[2] = T(P(2));\n\n    // Convert quaternion from Eigen convention (x, y, z, w)\n    // to Ceres convention (w, x, y, z)\n    T q_ceres[4] = {q[3], q[0], q[1], q[2]};\n\n    T P_c[3];\n    ceres::QuaternionRotatePoint(q_ceres, P_w, P_c);\n\n    P_c[0] += t[0];\n    P_c[1] += t[1];\n    P_c[2] += t[2];\n\n    // project 3D object point to the image plane\n    T k1 = params[0];\n    T k2 = params[1];\n    T p1 = params[2];\n    T p2 = params[3];\n    T fx = params[4];\n    T fy = params[5];\n    T alpha = T(0); //cameraParams.alpha();\n    T cx = params[6];\n    T cy = params[7];\n\n    // Transform to model plane\n    T u = P_c[0] / P_c[2];\n    T v = P_c[1] / P_c[2];\n\n    T rho_sqr = u * u + v * v;\n    T L = T(1.0) + k1 * rho_sqr + k2 * rho_sqr * rho_sqr;\n    T du = T(2.0) * p1 * u * v + p2 * (rho_sqr + T(2.0) * u * u);\n    T dv = p1 * (rho_sqr + T(2.0) * v * v) + T(2.0) * p2 * u * v;\n\n    u = L * u + du;\n    v = L * v + dv;\n    p(0) = fx * (u + alpha * v) + cx;\n    p(1) = fy * v + cy;\n}\n\n}\n\n#endif\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/camera_models/ScaramuzzaCamera.h",
    "content": "#ifndef SCARAMUZZACAMERA_H\r\n#define SCARAMUZZACAMERA_H\r\n\r\n#include <opencv2/core/core.hpp>\r\n#include <string>\r\n\r\n#include \"ceres/rotation.h\"\r\n#include \"Camera.h\"\r\n\r\nnamespace camodocal\r\n{\r\n\r\n#define SCARAMUZZA_POLY_SIZE 5\r\n#define SCARAMUZZA_INV_POLY_SIZE 20\r\n\r\n#define SCARAMUZZA_CAMERA_NUM_PARAMS (SCARAMUZZA_POLY_SIZE + SCARAMUZZA_INV_POLY_SIZE + 2 /*center*/ + 3 /*affine*/)\r\n\r\n/**\r\n * Scaramuzza Camera (Omnidirectional)\r\n * https://sites.google.com/site/scarabotix/ocamcalib-toolbox\r\n */\r\n\r\nclass OCAMCamera: public Camera\r\n{\r\npublic:\r\n    class Parameters: public Camera::Parameters\r\n    {\r\n    public:\r\n        Parameters();\r\n\r\n        double& C(void) { return m_C; }\r\n        double& D(void) { return m_D; }\r\n        double& E(void) { return m_E; }\r\n\r\n        double& center_x(void) { return m_center_x; }\r\n        double& center_y(void) { return m_center_y; }\r\n\r\n        double& poly(int idx) { return m_poly[idx]; }\r\n        double& inv_poly(int idx) { return m_inv_poly[idx]; }\r\n\r\n        double C(void) const { return m_C; }\r\n        double D(void) const { return m_D; }\r\n        double E(void) const { return m_E; }\r\n\r\n        double center_x(void) const { return m_center_x; }\r\n        double center_y(void) const { return m_center_y; }\r\n\r\n        double poly(int idx) const { return m_poly[idx]; }\r\n        double inv_poly(int idx) const { return m_inv_poly[idx]; }\r\n\r\n        bool readFromYamlFile(const std::string& filename);\r\n        void writeToYamlFile(const std::string& filename) const;\r\n\r\n        Parameters& operator=(const Parameters& other);\r\n        friend std::ostream& operator<< (std::ostream& out, const Parameters& params);\r\n\r\n    private:\r\n        double m_poly[SCARAMUZZA_POLY_SIZE];\r\n        double m_inv_poly[SCARAMUZZA_INV_POLY_SIZE];\r\n        double m_C;\r\n        double m_D;\r\n        double m_E;\r\n        double m_center_x;\r\n        double m_center_y;\r\n    };\r\n\r\n    OCAMCamera();\r\n\r\n    /**\r\n    * \\brief Constructor from the projection model parameters\r\n    */\r\n    OCAMCamera(const Parameters& params);\r\n\r\n    Camera::ModelType modelType(void) const;\r\n    const std::string& cameraName(void) const;\r\n    int imageWidth(void) const;\r\n    int imageHeight(void) const;\r\n\r\n    void estimateIntrinsics(const cv::Size& boardSize,\r\n                            const std::vector< std::vector<cv::Point3f> >& objectPoints,\r\n                            const std::vector< std::vector<cv::Point2f> >& imagePoints);\r\n\r\n    // Lift points from the image plane to the sphere\r\n    void liftSphere(const Eigen::Vector2d& p, Eigen::Vector3d& P) const;\r\n    //%output P\r\n\r\n    // Lift points from the image plane to the projective space\r\n    void liftProjective(const Eigen::Vector2d& p, Eigen::Vector3d& P) const;\r\n    //%output P\r\n\r\n    // Projects 3D points to the image plane (Pi function)\r\n    void spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p) const;\r\n    //%output p\r\n\r\n    // Projects 3D points to the image plane (Pi function)\r\n    // and calculates jacobian\r\n    //void spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p,\r\n    //                  Eigen::Matrix<double,2,3>& J) const;\r\n    //%output p\r\n    //%output J\r\n\r\n    void undistToPlane(const Eigen::Vector2d& p_u, Eigen::Vector2d& p) const;\r\n    //%output p\r\n\r\n    template <typename T>\r\n    static void spaceToPlane(const T* const params,\r\n                             const T* const q, const T* const t,\r\n                             const Eigen::Matrix<T, 3, 1>& P,\r\n                             Eigen::Matrix<T, 2, 1>& p);\r\n    template <typename T>\r\n    static void spaceToSphere(const T* const params,\r\n                              const T* const q, const T* const t,\r\n                              const Eigen::Matrix<T, 3, 1>& P,\r\n                              Eigen::Matrix<T, 3, 1>& P_s);\r\n    template <typename T>\r\n    static void LiftToSphere(const T* const params,\r\n                              const Eigen::Matrix<T, 2, 1>& p,\r\n                              Eigen::Matrix<T, 3, 1>& P);\r\n\r\n    template <typename T>\r\n    static void SphereToPlane(const T* const params, const Eigen::Matrix<T, 3, 1>& P,\r\n                               Eigen::Matrix<T, 2, 1>& p);\r\n\r\n\r\n    void initUndistortMap(cv::Mat& map1, cv::Mat& map2, double fScale = 1.0) const;\r\n    cv::Mat initUndistortRectifyMap(cv::Mat& map1, cv::Mat& map2,\r\n                                    float fx = -1.0f, float fy = -1.0f,\r\n                                    cv::Size imageSize = cv::Size(0, 0),\r\n                                    float cx = -1.0f, float cy = -1.0f,\r\n                                    cv::Mat rmat = cv::Mat::eye(3, 3, CV_32F)) const;\r\n\r\n    int parameterCount(void) const;\r\n\r\n    const Parameters& getParameters(void) const;\r\n    void setParameters(const Parameters& parameters);\r\n\r\n    void readParameters(const std::vector<double>& parameterVec);\r\n    void writeParameters(std::vector<double>& parameterVec) const;\r\n\r\n    void writeParametersToYamlFile(const std::string& filename) const;\r\n\r\n    std::string parametersToString(void) const;\r\n\r\nprivate:\r\n    Parameters mParameters;\r\n\r\n    double m_inv_scale;\r\n};\r\n\r\ntypedef boost::shared_ptr<OCAMCamera> OCAMCameraPtr;\r\ntypedef boost::shared_ptr<const OCAMCamera> OCAMCameraConstPtr;\r\n\r\ntemplate <typename T>\r\nvoid\r\nOCAMCamera::spaceToPlane(const T* const params,\r\n                         const T* const q, const T* const t,\r\n                         const Eigen::Matrix<T, 3, 1>& P,\r\n                         Eigen::Matrix<T, 2, 1>& p)\r\n{\r\n    T P_c[3];\r\n    {\r\n        T P_w[3];\r\n        P_w[0] = T(P(0));\r\n        P_w[1] = T(P(1));\r\n        P_w[2] = T(P(2));\r\n\r\n        // Convert quaternion from Eigen convention (x, y, z, w)\r\n        // to Ceres convention (w, x, y, z)\r\n        T q_ceres[4] = {q[3], q[0], q[1], q[2]};\r\n\r\n        ceres::QuaternionRotatePoint(q_ceres, P_w, P_c);\r\n\r\n        P_c[0] += t[0];\r\n        P_c[1] += t[1];\r\n        P_c[2] += t[2];\r\n    }\r\n\r\n    T c = params[0];\r\n    T d = params[1];\r\n    T e = params[2];\r\n    T xc[2] = { params[3], params[4] };\r\n\r\n    //T poly[SCARAMUZZA_POLY_SIZE];\r\n    //for (int i=0; i < SCARAMUZZA_POLY_SIZE; i++)\r\n    //    poly[i] = params[5+i];\r\n\r\n    T inv_poly[SCARAMUZZA_INV_POLY_SIZE];\r\n    for (int i=0; i < SCARAMUZZA_INV_POLY_SIZE; i++)\r\n        inv_poly[i] = params[5 + SCARAMUZZA_POLY_SIZE + i];\r\n\r\n    T norm_sqr = P_c[0] * P_c[0] + P_c[1] * P_c[1];\r\n    T norm = T(0.0);\r\n    if (norm_sqr > T(0.0))\r\n        norm = sqrt(norm_sqr);\r\n\r\n    T theta = atan2(-P_c[2], norm);\r\n    T rho = T(0.0);\r\n    T theta_i = T(1.0);\r\n\r\n    for (int i = 0; i < SCARAMUZZA_INV_POLY_SIZE; i++)\r\n    {\r\n        rho += theta_i * inv_poly[i];\r\n        theta_i *= theta;\r\n    }\r\n\r\n    T invNorm = T(1.0) / norm;\r\n    T xn[2] = {\r\n        P_c[0] * invNorm * rho,\r\n        P_c[1] * invNorm * rho\r\n    };\r\n\r\n    p(0) = xn[0] * c + xn[1] * d + xc[0];\r\n    p(1) = xn[0] * e + xn[1]     + xc[1];\r\n}\r\n\r\ntemplate <typename T>\r\nvoid\r\nOCAMCamera::spaceToSphere(const T* const params,\r\n                          const T* const q, const T* const t,\r\n                          const Eigen::Matrix<T, 3, 1>& P,\r\n                          Eigen::Matrix<T, 3, 1>& P_s)\r\n{\r\n    T P_c[3];\r\n    {\r\n        T P_w[3];\r\n        P_w[0] = T(P(0));\r\n        P_w[1] = T(P(1));\r\n        P_w[2] = T(P(2));\r\n\r\n        // Convert quaternion from Eigen convention (x, y, z, w)\r\n        // to Ceres convention (w, x, y, z)\r\n        T q_ceres[4] = {q[3], q[0], q[1], q[2]};\r\n\r\n        ceres::QuaternionRotatePoint(q_ceres, P_w, P_c);\r\n\r\n        P_c[0] += t[0];\r\n        P_c[1] += t[1];\r\n        P_c[2] += t[2];\r\n    }\r\n\r\n    //T poly[SCARAMUZZA_POLY_SIZE];\r\n    //for (int i=0; i < SCARAMUZZA_POLY_SIZE; i++)\r\n    //    poly[i] = params[5+i];\r\n\r\n    T norm_sqr = P_c[0] * P_c[0] + P_c[1] * P_c[1] + P_c[2] * P_c[2];\r\n    T norm = T(0.0);\r\n    if (norm_sqr > T(0.0))\r\n        norm = sqrt(norm_sqr);\r\n\r\n    P_s(0) = P_c[0] / norm;\r\n    P_s(1) = P_c[1] / norm;\r\n    P_s(2) = P_c[2] / norm;\r\n}\r\n\r\ntemplate <typename T>\r\nvoid\r\nOCAMCamera::LiftToSphere(const T* const params,\r\n                          const Eigen::Matrix<T, 2, 1>& p,\r\n                          Eigen::Matrix<T, 3, 1>& P)\r\n{\r\n    T c = params[0];\r\n    T d = params[1];\r\n    T e = params[2];\r\n    T cc[2] = { params[3], params[4] };\r\n    T poly[SCARAMUZZA_POLY_SIZE];\r\n    for (int i=0; i < SCARAMUZZA_POLY_SIZE; i++)\r\n       poly[i] = params[5+i];\r\n\r\n    // Relative to Center\r\n    T p_2d[2];\r\n    p_2d[0] = T(p(0));\r\n    p_2d[1] = T(p(1));\r\n\r\n    T xc[2] = { p_2d[0] - cc[0], p_2d[1] - cc[1]};\r\n\r\n    T inv_scale = T(1.0) / (c - d * e);\r\n\r\n    // Affine Transformation\r\n    T xc_a[2];\r\n\r\n    xc_a[0] = inv_scale * (xc[0] - d * xc[1]);\r\n    xc_a[1] = inv_scale * (-e * xc[0] + c * xc[1]);\r\n\r\n    T norm_sqr = xc_a[0] * xc_a[0] + xc_a[1] * xc_a[1];\r\n    T phi = sqrt(norm_sqr);\r\n    T phi_i = T(1.0);\r\n    T z = T(0.0);\r\n\r\n    for (int i = 0; i < SCARAMUZZA_POLY_SIZE; i++)\r\n    {\r\n        if (i!=1) {\r\n            z += phi_i * poly[i];\r\n        }\r\n        phi_i *= phi;\r\n    }\r\n\r\n    T p_3d[3];\r\n    p_3d[0] = xc[0];\r\n    p_3d[1] = xc[1];\r\n    p_3d[2] = -z;\r\n\r\n    T p_3d_norm_sqr = p_3d[0] * p_3d[0] + p_3d[1] * p_3d[1] + p_3d[2] * p_3d[2];\r\n    T p_3d_norm = sqrt(p_3d_norm_sqr);\r\n\r\n    P << p_3d[0] / p_3d_norm, p_3d[1] / p_3d_norm, p_3d[2] / p_3d_norm;\r\n}\r\n\r\ntemplate <typename T>\r\nvoid OCAMCamera::SphereToPlane(const T* const params, const Eigen::Matrix<T, 3, 1>& P,\r\n                               Eigen::Matrix<T, 2, 1>& p) {\r\n    T P_c[3];\r\n    {\r\n        P_c[0] = T(P(0));\r\n        P_c[1] = T(P(1));\r\n        P_c[2] = T(P(2));\r\n    }\r\n\r\n    T c = params[0];\r\n    T d = params[1];\r\n    T e = params[2];\r\n    T xc[2] = {params[3], params[4]};\r\n\r\n    T inv_poly[SCARAMUZZA_INV_POLY_SIZE];\r\n    for (int i = 0; i < SCARAMUZZA_INV_POLY_SIZE; i++)\r\n        inv_poly[i] = params[5 + SCARAMUZZA_POLY_SIZE + i];\r\n\r\n    T norm_sqr = P_c[0] * P_c[0] + P_c[1] * P_c[1];\r\n    T norm = T(0.0);\r\n    if (norm_sqr > T(0.0)) norm = sqrt(norm_sqr);\r\n\r\n    T theta = atan2(-P_c[2], norm);\r\n    T rho = T(0.0);\r\n    T theta_i = T(1.0);\r\n\r\n    for (int i = 0; i < SCARAMUZZA_INV_POLY_SIZE; i++) {\r\n        rho += theta_i * inv_poly[i];\r\n        theta_i *= theta;\r\n    }\r\n\r\n    T invNorm = T(1.0) / norm;\r\n    T xn[2] = {P_c[0] * invNorm * rho, P_c[1] * invNorm * rho};\r\n\r\n    p(0) = xn[0] * c + xn[1] * d + xc[0];\r\n    p(1) = xn[0] * e + xn[1] + xc[1];\r\n}\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/chessboard/Chessboard.h",
    "content": "#ifndef CHESSBOARD_H\n#define CHESSBOARD_H\n\n#include <boost/shared_ptr.hpp>\n#include <opencv2/core/core.hpp>\n\nnamespace camodocal\n{\n\n// forward declarations\nclass ChessboardCorner;\ntypedef boost::shared_ptr<ChessboardCorner> ChessboardCornerPtr;\nclass ChessboardQuad;\ntypedef boost::shared_ptr<ChessboardQuad> ChessboardQuadPtr;\n\nclass Chessboard\n{\npublic:\n    Chessboard(cv::Size boardSize, cv::Mat& image);\n\n    void findCorners(bool useOpenCV = false);\n    const std::vector<cv::Point2f>& getCorners(void) const;\n    bool cornersFound(void) const;\n\n    const cv::Mat& getImage(void) const;\n    const cv::Mat& getSketch(void) const;\n\nprivate:\n    bool findChessboardCorners(const cv::Mat& image,\n                               const cv::Size& patternSize,\n                               std::vector<cv::Point2f>& corners,\n                               int flags, bool useOpenCV);\n\n    bool findChessboardCornersImproved(const cv::Mat& image,\n                                       const cv::Size& patternSize,\n                                       std::vector<cv::Point2f>& corners,\n                                       int flags);\n\n    void cleanFoundConnectedQuads(std::vector<ChessboardQuadPtr>& quadGroup, cv::Size patternSize);\n\n    void findConnectedQuads(std::vector<ChessboardQuadPtr>& quads,\n                            std::vector<ChessboardQuadPtr>& group,\n                            int group_idx, int dilation);\n\n//    int checkQuadGroup(std::vector<ChessboardQuadPtr>& quadGroup,\n//                       std::vector<ChessboardCornerPtr>& outCorners,\n//                       cv::Size patternSize);\n\n    void labelQuadGroup(std::vector<ChessboardQuadPtr>& quad_group,\n                        cv::Size patternSize, bool firstRun);\n\n    void findQuadNeighbors(std::vector<ChessboardQuadPtr>& quads, int dilation);\n\n    int augmentBestRun(std::vector<ChessboardQuadPtr>& candidateQuads, int candidateDilation,\n                       std::vector<ChessboardQuadPtr>& existingQuads, int existingDilation);\n\n    void generateQuads(std::vector<ChessboardQuadPtr>& quads,\n                       cv::Mat& image, int flags,\n                       int dilation, bool firstRun);\n\n    bool checkQuadGroup(std::vector<ChessboardQuadPtr>& quads,\n                        std::vector<ChessboardCornerPtr>& corners,\n                        cv::Size patternSize);\n\n    void getQuadrangleHypotheses(const std::vector< std::vector<cv::Point> >& contours,\n                                 std::vector< std::pair<float, int> >& quads,\n                                 int classId) const;\n\n    bool checkChessboard(const cv::Mat& image, cv::Size patternSize) const;\n\n    bool checkBoardMonotony(std::vector<ChessboardCornerPtr>& corners,\n                            cv::Size patternSize);\n\n    bool matchCorners(ChessboardQuadPtr& quad1, int corner1,\n                      ChessboardQuadPtr& quad2, int corner2) const;\n\n    cv::Mat mImage;\n    cv::Mat mSketch;\n    std::vector<cv::Point2f> mCorners;\n    cv::Size mBoardSize;\n    bool mCornersFound;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/chessboard/ChessboardCorner.h",
    "content": "#ifndef CHESSBOARDCORNER_H\n#define CHESSBOARDCORNER_H\n\n#include <boost/shared_ptr.hpp>\n#include <opencv2/core/core.hpp>\n\nnamespace camodocal\n{\n\nclass ChessboardCorner;\ntypedef boost::shared_ptr<ChessboardCorner> ChessboardCornerPtr;\n\nclass ChessboardCorner\n{\npublic:\n    ChessboardCorner() : row(0), column(0), needsNeighbor(true), count(0) {}\n\n    float meanDist(int &n) const\n    {\n        float sum = 0;\n        n = 0;\n        for (int i = 0; i < 4; ++i)\n        {\n            if (neighbors[i].get())\n            {\n                float dx = neighbors[i]->pt.x - pt.x;\n                float dy = neighbors[i]->pt.y - pt.y;\n                sum += sqrt(dx*dx + dy*dy);\n                n++;\n            }\n        }\n        return sum / std::max(n, 1);\n    }\n\n    cv::Point2f pt;                     // X and y coordinates\n    int row;                            // Row and column of the corner\n    int column;                         // in the found pattern\n    bool needsNeighbor;                 // Does the corner require a neighbor?\n    int count;                          // number of corner neighbors\n    ChessboardCornerPtr neighbors[4];   // pointer to all corner neighbors\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/chessboard/ChessboardQuad.h",
    "content": "#ifndef CHESSBOARDQUAD_H\n#define CHESSBOARDQUAD_H\n\n#include <boost/shared_ptr.hpp>\n\n#include \"camodocal/chessboard/ChessboardCorner.h\"\n\nnamespace camodocal\n{\n\nclass ChessboardQuad;\ntypedef boost::shared_ptr<ChessboardQuad> ChessboardQuadPtr;\n\nclass ChessboardQuad\n{\npublic:\n    ChessboardQuad() : count(0), group_idx(-1), edge_len(FLT_MAX), labeled(false) {}\n\n    int count;                         // Number of quad neighbors\n    int group_idx;                     // Quad group ID\n    float edge_len;                    // Smallest side length^2\n    ChessboardCornerPtr corners[4];    // Coordinates of quad corners\n    ChessboardQuadPtr neighbors[4];    // Pointers of quad neighbors\n    bool labeled;                      // Has this corner been labeled?\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/chessboard/Spline.h",
    "content": "/*  dynamo:- Event driven molecular dynamics simulator\n    http://www.marcusbannerman.co.uk/dynamo\n    Copyright (C) 2011  Marcus N Campbell Bannerman <m.bannerman@gmail.com>\n\n    This program is free software: you can redistribute it and/or\n    modify it under the terms of the GNU General Public License\n    version 3 as published by the Free Software Foundation.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <boost/numeric/ublas/vector.hpp>\n#include <boost/numeric/ublas/vector_proxy.hpp>\n#include <boost/numeric/ublas/matrix.hpp>\n#include <boost/numeric/ublas/triangular.hpp>\n#include <boost/numeric/ublas/lu.hpp>\n#include <exception>\n\nnamespace ublas = boost::numeric::ublas;\n\nclass Spline : private std::vector<std::pair<double, double> >\n{\npublic:\n  //The boundary conditions available\n  enum BC_type {\n\tFIXED_1ST_DERIV_BC,\n\tFIXED_2ND_DERIV_BC,\n\tPARABOLIC_RUNOUT_BC\n  };\n\n  enum Spline_type {\n\tLINEAR,\n\tCUBIC\n  };\n\n  //Constructor takes the boundary conditions as arguments, this\n  //sets the first derivative (gradient) at the lower and upper\n  //end points\n  Spline():\n\t_valid(false),\n\t_BCLow(FIXED_2ND_DERIV_BC), _BCHigh(FIXED_2ND_DERIV_BC),\n\t_BCLowVal(0), _BCHighVal(0),\n\t_type(CUBIC)\n  {}\n\n  typedef std::vector<std::pair<double, double> > base;\n  typedef base::const_iterator const_iterator;\n\n  //Standard STL read-only container stuff\n  const_iterator begin() const { return base::begin(); }\n  const_iterator end() const { return base::end(); }\n  void clear() { _valid = false; base::clear(); _data.clear(); }\n  size_t size() const { return base::size(); }\n  size_t max_size() const { return base::max_size(); }\n  size_t capacity() const { return base::capacity(); }\n  bool empty() const { return base::empty(); }\n\n  //Add a point to the spline, and invalidate it so its\n  //recalculated on the next access\n  inline void addPoint(double x, double y)\n  {\n\t_valid = false;\n\tbase::push_back(std::pair<double, double>(x,y));\n  }\n\n  //Reset the boundary conditions\n  inline void setLowBC(BC_type BC, double val = 0)\n  { _BCLow = BC; _BCLowVal = val; _valid = false; }\n\n  inline void setHighBC(BC_type BC, double val = 0)\n  { _BCHigh = BC; _BCHighVal = val; _valid = false; }\n\n  void setType(Spline_type type) { _type = type; _valid = false; }\n\n  //Check if the spline has been calculated, then generate the\n  //spline interpolated value\n  double operator()(double xval)\n  {\n\tif (!_valid) generate();\n\n\t//Special cases when we're outside the range of the spline points\n\tif (xval <= x(0)) return lowCalc(xval);\n\tif (xval >= x(size()-1)) return highCalc(xval);\n\n\t//Check all intervals except the last one\n\tfor (std::vector<SplineData>::const_iterator iPtr = _data.begin();\n\t\t iPtr != _data.end()-1; ++iPtr)\n\t\tif ((xval >= iPtr->x) && (xval <= (iPtr+1)->x))\n\t\t  return splineCalc(iPtr, xval);\n\n\treturn splineCalc(_data.end() - 1, xval);\n  }\n\nprivate:\n\n  ///////PRIVATE DATA MEMBERS\n  struct SplineData { double x,a,b,c,d; };\n  //vector of calculated spline data\n  std::vector<SplineData> _data;\n  //Second derivative at each point\n  ublas::vector<double> _ddy;\n  //Tracks whether the spline parameters have been calculated for\n  //the current set of points\n  bool _valid;\n  //The boundary conditions\n  BC_type _BCLow, _BCHigh;\n  //The values of the boundary conditions\n  double _BCLowVal, _BCHighVal;\n\n  Spline_type _type;\n\n  ///////PRIVATE FUNCTIONS\n  //Function to calculate the value of a given spline at a point xval\n  inline double splineCalc(std::vector<SplineData>::const_iterator i, double xval)\n  {\n\tconst double lx = xval - i->x;\n\treturn ((i->a * lx + i->b) * lx + i->c) * lx + i->d;\n  }\n\n  inline double lowCalc(double xval)\n  {\n\tconst double lx = xval - x(0);\n\n\tif (_type == LINEAR)\n\t  return lx * _BCHighVal + y(0);\n\n\tconst double firstDeriv = (y(1) - y(0)) / h(0) - 2 * h(0) * (_data[0].b + 2 * _data[1].b) / 6;\n\n\tswitch(_BCLow)\n\t  {\n\t  case FIXED_1ST_DERIV_BC:\n\t\treturn lx * _BCLowVal + y(0);\n\t  case FIXED_2ND_DERIV_BC:\n\t\t  return lx * lx * _BCLowVal + firstDeriv * lx + y(0);\n\t  case PARABOLIC_RUNOUT_BC:\n\t\treturn lx * lx * _ddy[0] + lx * firstDeriv  + y(0);\n\t  }\n\tthrow std::runtime_error(\"Unknown BC\");\n  }\n\n  inline double highCalc(double xval)\n  {\n\tconst double lx = xval - x(size() - 1);\n\n\tif (_type == LINEAR)\n\t  return lx * _BCHighVal + y(size() - 1);\n\n\tconst double firstDeriv = 2 * h(size() - 2) * (_ddy[size() - 2] + 2 * _ddy[size() - 1]) / 6 + (y(size() - 1) - y(size() - 2)) / h(size() - 2);\n\n\tswitch(_BCHigh)\n\t  {\n\t  case FIXED_1ST_DERIV_BC:\n\t\treturn lx * _BCHighVal + y(size() - 1);\n\t  case FIXED_2ND_DERIV_BC:\n\t\treturn lx * lx * _BCHighVal + firstDeriv * lx + y(size() - 1);\n\t  case PARABOLIC_RUNOUT_BC:\n\t\treturn lx * lx * _ddy[size()-1] + lx * firstDeriv  + y(size() - 1);\n\t  }\n\tthrow std::runtime_error(\"Unknown BC\");\n  }\n\n  //These just provide access to the point data in a clean way\n  inline double x(size_t i) const { return operator[](i).first; }\n  inline double y(size_t i) const { return operator[](i).second; }\n  inline double h(size_t i) const { return x(i+1) - x(i); }\n\n  //Invert a arbitrary matrix using the boost ublas library\n  template<class T>\n  bool InvertMatrix(ublas::matrix<T> A,\n\t\tublas::matrix<T>& inverse)\n  {\n\tusing namespace ublas;\n\n\t// create a permutation matrix for the LU-factorization\n\tpermutation_matrix<std::size_t> pm(A.size1());\n\n\t// perform LU-factorization\n\tint res = lu_factorize(A,pm);\n\t\tif( res != 0 ) return false;\n\n\t// create identity matrix of \"inverse\"\n\tinverse.assign(ublas::identity_matrix<T>(A.size1()));\n\n\t// backsubstitute to get the inverse\n\tlu_substitute(A, pm, inverse);\n\n\treturn true;\n  }\n\n  //This function will recalculate the spline parameters and store\n  //them in _data, ready for spline interpolation\n  void generate()\n  {\n\tif (size() < 2)\n\t  throw std::runtime_error(\"Spline requires at least 2 points\");\n\n\t//If any spline points are at the same x location, we have to\n\t//just slightly seperate them\n\t{\n\t  bool testPassed(false);\n\t  while (!testPassed)\n\t\t{\n\t\t  testPassed = true;\n\t\t  std::sort(base::begin(), base::end());\n\n\t\t  for (base::iterator iPtr = base::begin(); iPtr != base::end() - 1; ++iPtr)\n\t\tif (iPtr->first == (iPtr+1)->first)\n\t\t  {\n\t\t\tif ((iPtr+1)->first != 0)\n\t\t\t  (iPtr+1)->first += (iPtr+1)->first\n\t\t\t* std::numeric_limits<double>::epsilon() * 10;\n\t\t\telse\n\t\t\t  (iPtr+1)->first = std::numeric_limits<double>::epsilon() * 10;\n\t\t\ttestPassed = false;\n\t\t\tbreak;\n\t\t  }\n\t\t}\n\t}\n\n\tconst size_t e = size() - 1;\n\n\tswitch (_type)\n\t  {\n\t  case LINEAR:\n\t\t{\n\t\t  _data.resize(e);\n\t\t  for (size_t i(0); i < e; ++i)\n\t\t{\n\t\t  _data[i].x = x(i);\n\t\t  _data[i].a = 0;\n\t\t  _data[i].b = 0;\n\t\t  _data[i].c = (y(i+1) - y(i)) / (x(i+1) - x(i));\n\t\t  _data[i].d = y(i);\n\t\t}\n\t\t  break;\n\t\t}\n\t  case CUBIC:\n\t\t{\n\t\t  ublas::matrix<double> A(size(), size());\n\t\t  for (size_t yv(0); yv <= e; ++yv)\n\t\tfor (size_t xv(0); xv <= e; ++xv)\n\t\t  A(xv,yv) = 0;\n\n\t\t  for (size_t i(1); i < e; ++i)\n\t\t{\n\t\t  A(i-1,i) = h(i-1);\n\t\t  A(i,i) = 2 * (h(i-1) + h(i));\n\t\t  A(i+1,i) = h(i);\n\t\t}\n\n\t\t  ublas::vector<double> C(size());\n\t\t  for (size_t xv(0); xv <= e; ++xv)\n\t\tC(xv) = 0;\n\n\t\t  for (size_t i(1); i < e; ++i)\n\t\tC(i) = 6 *\n\t\t  ((y(i+1) - y(i)) / h(i)\n\t\t   - (y(i) - y(i-1)) / h(i-1));\n\n\t\t  //Boundary conditions\n\t\t  switch(_BCLow)\n\t\t{\n\t\tcase FIXED_1ST_DERIV_BC:\n\t\t  C(0) = 6 * ((y(1) - y(0)) / h(0) - _BCLowVal);\n\t\t  A(0,0) = 2 * h(0);\n\t\t  A(1,0) = h(0);\n\t\t  break;\n\t\tcase FIXED_2ND_DERIV_BC:\n\t\t  C(0) = _BCLowVal;\n\t\t  A(0,0) = 1;\n\t\t  break;\n\t\tcase PARABOLIC_RUNOUT_BC:\n\t\t  C(0) = 0; A(0,0) = 1; A(1,0) = -1;\n\t\t  break;\n\t\t}\n\n\t\t  switch(_BCHigh)\n\t\t{\n\t\tcase FIXED_1ST_DERIV_BC:\n\t\t  C(e) = 6 * (_BCHighVal - (y(e) - y(e-1)) / h(e-1));\n\t\t  A(e,e) = 2 * h(e - 1);\n\t\t  A(e-1,e) = h(e - 1);\n\t\t  break;\n\t\tcase FIXED_2ND_DERIV_BC:\n\t\t  C(e) = _BCHighVal;\n\t\t  A(e,e) = 1;\n\t\t  break;\n\t\tcase PARABOLIC_RUNOUT_BC:\n\t\t  C(e) = 0; A(e,e) = 1; A(e-1,e) = -1;\n\t\t  break;\n\t\t}\n\n\t\t  ublas::matrix<double> AInv(size(), size());\n\t\t  InvertMatrix(A,AInv);\n\n\t\t  _ddy = ublas::prod(C, AInv);\n\n\t\t  _data.resize(size()-1);\n\t\t  for (size_t i(0); i < e; ++i)\n\t\t{\n\t\t  _data[i].x = x(i);\n\t\t  _data[i].a = (_ddy(i+1) - _ddy(i)) / (6 * h(i));\n\t\t  _data[i].b = _ddy(i) / 2;\n\t\t  _data[i].c = (y(i+1) - y(i)) / h(i) - _ddy(i+1) * h(i) / 6 - _ddy(i) * h(i) / 3;\n\t\t  _data[i].d = y(i);\n\t\t}\n\t\t}\n\t  }\n\t_valid = true;\n  }\n};\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/gpl/EigenQuaternionParameterization.h",
    "content": "#ifndef EIGENQUATERNIONPARAMETERIZATION_H\n#define EIGENQUATERNIONPARAMETERIZATION_H\n\n#include \"ceres/local_parameterization.h\"\n\nnamespace camodocal\n{\n\nclass EigenQuaternionParameterization : public ceres::LocalParameterization\n{\npublic:\n    virtual ~EigenQuaternionParameterization() {}\n    virtual bool Plus(const double* x,\n                      const double* delta,\n                      double* x_plus_delta) const;\n    virtual bool ComputeJacobian(const double* x,\n                                 double* jacobian) const;\n    virtual int GlobalSize() const { return 4; }\n    virtual int LocalSize() const { return 3; }\n\nprivate:\n    template<typename T>\n    void EigenQuaternionProduct(const T z[4], const T w[4], T zw[4]) const;\n};\n\n\ntemplate<typename T>\nvoid\nEigenQuaternionParameterization::EigenQuaternionProduct(const T z[4], const T w[4], T zw[4]) const\n{\n    zw[0] = z[3] * w[0] + z[0] * w[3] + z[1] * w[2] - z[2] * w[1];\n    zw[1] = z[3] * w[1] - z[0] * w[2] + z[1] * w[3] + z[2] * w[0];\n    zw[2] = z[3] * w[2] + z[0] * w[1] - z[1] * w[0] + z[2] * w[3];\n    zw[3] = z[3] * w[3] - z[0] * w[0] - z[1] * w[1] - z[2] * w[2];\n}\n\n}\n\n#endif\n\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/gpl/EigenUtils.h",
    "content": "#ifndef EIGENUTILS_H\n#define EIGENUTILS_H\n\n#include <eigen3/Eigen/Dense>\n\n#include \"ceres/rotation.h\"\n#include \"camodocal/gpl/gpl.h\"\n\nnamespace camodocal\n{\n\n// Returns the 3D cross product skew symmetric matrix of a given 3D vector\ntemplate<typename T>\nEigen::Matrix<T, 3, 3> skew(const Eigen::Matrix<T, 3, 1>& vec)\n{\n    return (Eigen::Matrix<T, 3, 3>() << T(0), -vec(2), vec(1),\n                                        vec(2), T(0), -vec(0),\n                                        -vec(1), vec(0), T(0)).finished();\n}\n\ntemplate<typename Derived>\ntypename Eigen::MatrixBase<Derived>::PlainObject sqrtm(const Eigen::MatrixBase<Derived>& A)\n{\n    Eigen::SelfAdjointEigenSolver<typename Derived::PlainObject> es(A);\n\n    return es.operatorSqrt();\n}\n\ntemplate<typename T>\nEigen::Matrix<T, 3, 3> AngleAxisToRotationMatrix(const Eigen::Matrix<T, 3, 1>& rvec)\n{\n    T angle = rvec.norm();\n    if (angle == T(0))\n    {\n        return Eigen::Matrix<T, 3, 3>::Identity();\n    }\n\n    Eigen::Matrix<T, 3, 1> axis;\n    axis = rvec.normalized();\n\n    Eigen::Matrix<T, 3, 3> rmat;\n    rmat = Eigen::AngleAxis<T>(angle, axis);\n\n    return rmat;\n}\n\ntemplate<typename T>\nEigen::Quaternion<T> AngleAxisToQuaternion(const Eigen::Matrix<T, 3, 1>& rvec)\n{\n    Eigen::Matrix<T, 3, 3> rmat = AngleAxisToRotationMatrix<T>(rvec);\n\n    return Eigen::Quaternion<T>(rmat);\n}\n\ntemplate<typename T>\nvoid AngleAxisToQuaternion(const Eigen::Matrix<T, 3, 1>& rvec, T* q)\n{\n    Eigen::Quaternion<T> quat = AngleAxisToQuaternion<T>(rvec);\n\n    q[0] = quat.x();\n    q[1] = quat.y();\n    q[2] = quat.z();\n    q[3] = quat.w();\n}\n\ntemplate<typename T>\nEigen::Matrix<T, 3, 1> RotationToAngleAxis(const Eigen::Matrix<T, 3, 3> & rmat)\n{\n    Eigen::AngleAxis<T> angleaxis; \n    angleaxis.fromRotationMatrix(rmat); \n    return angleaxis.angle() * angleaxis.axis(); \n    \n}\n\ntemplate<typename T>\nvoid QuaternionToAngleAxis(const T* const q, Eigen::Matrix<T, 3, 1>& rvec)\n{\n    Eigen::Quaternion<T> quat(q[3], q[0], q[1], q[2]);\n\n    Eigen::Matrix<T, 3, 3> rmat = quat.toRotationMatrix();\n\n    Eigen::AngleAxis<T> angleaxis;\n    angleaxis.fromRotationMatrix(rmat);\n\n    rvec = angleaxis.angle() * angleaxis.axis();\n}\n\ntemplate<typename T>\nEigen::Matrix<T, 3, 3> QuaternionToRotation(const T* const q)\n{\n    T R[9];\n    ceres::QuaternionToRotation(q, R);\n\n    Eigen::Matrix<T, 3, 3> rmat;\n    for (int i = 0; i < 3; ++i)\n    {\n        for (int j = 0; j < 3; ++j)\n        {\n            rmat(i,j) = R[i * 3 + j];\n        }\n    }\n\n    return rmat;\n}\n\ntemplate<typename T>\nvoid QuaternionToRotation(const T* const q, T* rot)\n{\n    ceres::QuaternionToRotation(q, rot);\n}\n\ntemplate<typename T>\nEigen::Matrix<T,4,4> QuaternionMultMatLeft(const Eigen::Quaternion<T>& q)\n{\n    return (Eigen::Matrix<T,4,4>() << q.w(), -q.z(), q.y(), q.x(),\n                                      q.z(), q.w(), -q.x(), q.y(),\n                                      -q.y(), q.x(), q.w(), q.z(),\n                                      -q.x(), -q.y(), -q.z(), q.w()).finished();\n}\n\ntemplate<typename T>\nEigen::Matrix<T,4,4> QuaternionMultMatRight(const Eigen::Quaternion<T>& q)\n{\n    return (Eigen::Matrix<T,4,4>() << q.w(), q.z(), -q.y(), q.x(),\n                                      -q.z(), q.w(), q.x(), q.y(),\n                                      q.y(), -q.x(), q.w(), q.z(),\n                                      -q.x(), -q.y(), -q.z(), q.w()).finished();\n}\n\n/// @param theta - rotation about screw axis\n/// @param d - projection of tvec on the rotation axis\n/// @param l - screw axis direction\n/// @param m - screw axis moment\ntemplate<typename T>\nvoid AngleAxisAndTranslationToScrew(const Eigen::Matrix<T, 3, 1>& rvec,\n                                    const Eigen::Matrix<T, 3, 1>& tvec,\n                                    T& theta, T& d,\n                                    Eigen::Matrix<T, 3, 1>& l,\n                                    Eigen::Matrix<T, 3, 1>& m)\n{\n\n    theta = rvec.norm();\n    if (theta == 0)\n    {\n        l.setZero(); \n        m.setZero(); \n        std::cout << \"Warning: Undefined screw! Returned 0. \" << std::endl; \n    }\n\n    l = rvec.normalized();\n\n    Eigen::Matrix<T, 3, 1> t = tvec;\n\n    d = t.transpose() * l;\n\n    // point on screw axis - projection of origin on screw axis\n    Eigen::Matrix<T, 3, 1> c;\n    c = 0.5 * (t - d * l + (1.0 / tan(theta / 2.0) * l).cross(t));\n\n    // c and hence the screw axis is not defined if theta is either 0 or M_PI\n    m = c.cross(l);\n}\n\ntemplate<typename T>\nEigen::Matrix<T, 3, 3> RPY2mat(T roll, T pitch, T yaw)\n{\n    Eigen::Matrix<T, 3, 3> m;\n\n    T cr = cos(roll);\n    T sr = sin(roll);\n    T cp = cos(pitch);\n    T sp = sin(pitch);\n    T cy = cos(yaw);\n    T sy = sin(yaw);\n\n    m(0,0) = cy * cp;\n    m(0,1) = cy * sp * sr - sy * cr;\n    m(0,2) = cy * sp * cr + sy * sr;\n    m(1,0) = sy * cp;\n    m(1,1) = sy * sp * sr + cy * cr;\n    m(1,2) = sy * sp * cr - cy * sr;\n    m(2,0) = - sp;\n    m(2,1) = cp * sr;\n    m(2,2) = cp * cr;\n    return m; \n}\n\ntemplate<typename T>\nvoid mat2RPY(const Eigen::Matrix<T, 3, 3>& m, T& roll, T& pitch, T& yaw)\n{\n    roll = atan2(m(2,1), m(2,2));\n    pitch = atan2(-m(2,0), sqrt(m(2,1) * m(2,1) + m(2,2) * m(2,2)));\n    yaw = atan2(m(1,0), m(0,0));\n}\n\ntemplate<typename T>\nEigen::Matrix<T, 4, 4> homogeneousTransform(const Eigen::Matrix<T, 3, 3>& R, const Eigen::Matrix<T, 3, 1>& t)\n{\n    Eigen::Matrix<T, 4, 4> H;\n    H.setIdentity();\n\n    H.block(0,0,3,3) = R;\n    H.block(0,3,3,1) = t;\n\n    return H;\n}\n\ntemplate<typename T>\nEigen::Matrix<T, 4, 4> poseWithCartesianTranslation(const T* const q, const T* const p)\n{\n    Eigen::Matrix<T, 4, 4> pose = Eigen::Matrix<T, 4, 4>::Identity();\n\n    T rotation[9];\n    ceres::QuaternionToRotation(q, rotation);\n    for (int i = 0; i < 3; ++i)\n    {\n        for (int j = 0; j < 3; ++j)\n        {\n            pose(i,j) = rotation[i * 3 + j];\n        }\n    }\n\n    pose(0,3) = p[0];\n    pose(1,3) = p[1];\n    pose(2,3) = p[2];\n\n    return pose;\n}\n\ntemplate<typename T>\nEigen::Matrix<T, 4, 4> poseWithSphericalTranslation(const T* const q, const T* const p, const T scale = T(1.0))\n{\n    Eigen::Matrix<T, 4, 4> pose = Eigen::Matrix<T, 4, 4>::Identity();\n\n    T rotation[9];\n    ceres::QuaternionToRotation(q, rotation);\n    for (int i = 0; i < 3; ++i)\n    {\n        for (int j = 0; j < 3; ++j)\n        {\n            pose(i,j) = rotation[i * 3 + j];\n        }\n    }\n\n    T theta = p[0];\n    T phi = p[1];\n    pose(0,3) = sin(theta) * cos(phi) * scale;\n    pose(1,3) = sin(theta) * sin(phi) * scale;\n    pose(2,3) = cos(theta) * scale;\n\n    return pose;\n}\n\n// Returns the Sampson error of a given essential matrix and 2 image points\ntemplate<typename T>\nT sampsonError(const Eigen::Matrix<T, 3, 3>& E,\n               const Eigen::Matrix<T, 3, 1>& p1,\n               const Eigen::Matrix<T, 3, 1>& p2)\n{\n    Eigen::Matrix<T, 3, 1> Ex1 = E * p1;\n    Eigen::Matrix<T, 3, 1> Etx2 = E.transpose() * p2;\n\n    T x2tEx1 = p2.dot(Ex1);\n\n    // compute Sampson error\n    T err = square(x2tEx1) / (square(Ex1(0,0)) + square(Ex1(1,0)) + square(Etx2(0,0)) + square(Etx2(1,0)));\n\n    return err;\n}\n\n// Returns the Sampson error of a given rotation/translation and 2 image points\ntemplate<typename T>\nT sampsonError(const Eigen::Matrix<T, 3, 3>& R,\n               const Eigen::Matrix<T, 3, 1>& t,\n               const Eigen::Matrix<T, 3, 1>& p1,\n               const Eigen::Matrix<T, 3, 1>& p2)\n{\n    // construct essential matrix\n    Eigen::Matrix<T, 3, 3> E = skew(t) * R;\n\n    Eigen::Matrix<T, 3, 1> Ex1 = E * p1;\n    Eigen::Matrix<T, 3, 1> Etx2 = E.transpose() * p2;\n\n    T x2tEx1 = p2.dot(Ex1);\n\n    // compute Sampson error\n    T err = square(x2tEx1) / (square(Ex1(0,0)) + square(Ex1(1,0)) + square(Etx2(0,0)) + square(Etx2(1,0)));\n\n    return err;\n}\n\n// Returns the Sampson error of a given rotation/translation and 2 image points\ntemplate<typename T>\nT sampsonError(const Eigen::Matrix<T, 4, 4>& H,\n               const Eigen::Matrix<T, 3, 1>& p1,\n               const Eigen::Matrix<T, 3, 1>& p2)\n{\n    Eigen::Matrix<T, 3, 3> R = H.block(0, 0, 3, 3);\n    Eigen::Matrix<T, 3, 1> t = H.block(0, 3, 3, 1);\n\n    return sampsonError(R, t, p1, p2);\n}\n\ntemplate<typename T>\nEigen::Matrix<T, 3, 1>\ntransformPoint(const Eigen::Matrix<T, 4, 4>& H, const Eigen::Matrix<T, 3, 1>& P)\n{\n    Eigen::Matrix<T, 3, 1> P_trans = H.block(0, 0, 3, 3) * P + H.block(0, 3, 3, 1);\n\n    return P_trans;\n}\n\ntemplate<typename T>\nEigen::Matrix<T, 4, 4>\nestimate3DRigidTransform(const std::vector<Eigen::Matrix<T, 3, 1>, Eigen::aligned_allocator<Eigen::Matrix<T, 3, 1> > >& points1,\n                         const std::vector<Eigen::Matrix<T, 3, 1>, Eigen::aligned_allocator<Eigen::Matrix<T, 3, 1> > >& points2)\n{\n    // compute centroids\n    Eigen::Matrix<T, 3, 1> c1, c2;\n    c1.setZero(); c2.setZero();\n\n    for (size_t i = 0; i < points1.size(); ++i)\n    {\n        c1 += points1.at(i);\n        c2 += points2.at(i);\n    }\n\n    c1 /= points1.size();\n    c2 /= points1.size();\n\n    Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> X(3, points1.size());\n    Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> Y(3, points1.size());\n    for (size_t i = 0; i < points1.size(); ++i)\n    {\n        X.col(i) = points1.at(i) - c1;\n        Y.col(i) = points2.at(i) - c2;\n    }\n\n    Eigen::Matrix<T, 3, 3> H = X * Y.transpose();\n\n    Eigen::JacobiSVD< Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> > svd(H, Eigen::ComputeFullU | Eigen::ComputeFullV);\n\n    Eigen::Matrix<T, 3, 3> U = svd.matrixU();\n    Eigen::Matrix<T, 3, 3> V = svd.matrixV();\n    if (U.determinant() * V.determinant() < 0.0)\n    {\n        V.col(2) *= -1.0;\n    }\n\n    Eigen::Matrix<T, 3, 3> R = V * U.transpose();\n    Eigen::Matrix<T, 3, 1> t = c2 - R * c1;\n\n    return homogeneousTransform(R, t);\n}\n\ntemplate<typename T>\nEigen::Matrix<T, 4, 4>\nestimate3DRigidSimilarityTransform(const std::vector<Eigen::Matrix<T, 3, 1>, Eigen::aligned_allocator<Eigen::Matrix<T, 3, 1> > >& points1,\n                                   const std::vector<Eigen::Matrix<T, 3, 1>, Eigen::aligned_allocator<Eigen::Matrix<T, 3, 1> > >& points2)\n{\n    // compute centroids\n    Eigen::Matrix<T, 3, 1> c1, c2;\n    c1.setZero(); c2.setZero();\n\n    for (size_t i = 0; i < points1.size(); ++i)\n    {\n        c1 += points1.at(i);\n        c2 += points2.at(i);\n    }\n\n    c1 /= points1.size();\n    c2 /= points1.size();\n\n    Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> X(3, points1.size());\n    Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> Y(3, points1.size());\n    for (size_t i = 0; i < points1.size(); ++i)\n    {\n        X.col(i) = points1.at(i) - c1;\n        Y.col(i) = points2.at(i) - c2;\n    }\n\n    Eigen::Matrix<T, 3, 3> H = X * Y.transpose();\n\n    Eigen::JacobiSVD< Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> > svd(H, Eigen::ComputeFullU | Eigen::ComputeFullV);\n\n    Eigen::Matrix<T, 3, 3> U = svd.matrixU();\n    Eigen::Matrix<T, 3, 3> V = svd.matrixV();\n    if (U.determinant() * V.determinant() < 0.0)\n    {\n        V.col(2) *= -1.0;\n    }\n\n    Eigen::Matrix<T, 3, 3> R = V * U.transpose();\n\n    std::vector<Eigen::Matrix<T, 3, 1>, Eigen::aligned_allocator<Eigen::Matrix<T, 3, 1> > > rotatedPoints1(points1.size());\n    for (size_t i = 0; i < points1.size(); ++i)\n    {\n        rotatedPoints1.at(i) = R * (points1.at(i) - c1);\n    }\n\n    double sum_ss = 0.0, sum_tt = 0.0;\n    for (size_t i = 0; i < points1.size(); ++i)\n    {\n        sum_ss += (points1.at(i) - c1).squaredNorm();\n        sum_tt += (points2.at(i) - c2).dot(rotatedPoints1.at(i));\n    }\n\n    double scale = sum_tt / sum_ss;\n\n    Eigen::Matrix<T, 3, 3> sR = scale * R;\n    Eigen::Matrix<T, 3, 1> t = c2 - sR * c1;\n\n    return homogeneousTransform(sR, t);\n}\n\n}\n\n#endif\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/gpl/gpl.h",
    "content": "#ifndef GPL_H\r\n#define GPL_H\r\n\r\n#include <algorithm>\r\n#include <cmath>\r\n#include <opencv2/core/core.hpp>\r\n\r\nnamespace camodocal\r\n{\r\n\r\ntemplate<class T>\r\nconst T clamp(const T& v, const T& a, const T& b)\r\n{\r\n\treturn std::min(b, std::max(a, v));\r\n}\r\n\r\ndouble hypot3(double x, double y, double z);\r\nfloat hypot3f(float x, float y, float z);\r\n\r\ntemplate<class T>\r\nconst T normalizeTheta(const T& theta)\r\n{\r\n\tT normTheta = theta;\r\n\r\n\twhile (normTheta < - M_PI)\r\n\t{\r\n\t\tnormTheta += 2.0 * M_PI;\r\n\t}\r\n\twhile (normTheta > M_PI)\r\n\t{\r\n\t\tnormTheta -= 2.0 * M_PI;\r\n\t}\r\n\r\n\treturn normTheta;\r\n}\r\n\r\ndouble d2r(double deg);\r\nfloat d2r(float deg);\r\ndouble r2d(double rad);\r\nfloat r2d(float rad);\r\n\r\ndouble sinc(double theta);\r\n\r\ntemplate<class T>\r\nconst T square(const T& x)\r\n{\r\n\treturn x * x;\r\n}\r\n\r\ntemplate<class T>\r\nconst T cube(const T& x)\r\n{\r\n\treturn x * x * x;\r\n}\r\n\r\ntemplate<class T>\r\nconst T random(const T& a, const T& b)\r\n{\r\n\treturn static_cast<double>(rand()) / RAND_MAX * (b - a) + a;\r\n}\r\n\r\ntemplate<class T>\r\nconst T randomNormal(const T& sigma)\r\n{\r\n    T x1, x2, w;\r\n\r\n    do\r\n    {\r\n        x1 = 2.0 * random(0.0, 1.0) - 1.0;\r\n        x2 = 2.0 * random(0.0, 1.0) - 1.0;\r\n        w = x1 * x1 + x2 * x2;\r\n    }\r\n    while (w >= 1.0 || w == 0.0);\r\n\r\n    w = sqrt((-2.0 * log(w)) / w);\r\n\r\n    return x1 * w * sigma;\r\n}\r\n\r\nunsigned long long timeInMicroseconds(void);\r\n\r\ndouble timeInSeconds(void);\r\n\r\nvoid colorDepthImage(cv::Mat& imgDepth,\r\n                     cv::Mat& imgColoredDepth,\r\n                     float minRange, float maxRange);\r\n\r\nbool colormap(const std::string& name, unsigned char idx,\r\n              float& r, float& g, float& b);\r\n\r\nstd::vector<cv::Point2i> bresLine(int x0, int y0, int x1, int y1);\r\nstd::vector<cv::Point2i> bresCircle(int x0, int y0, int r);\r\n\r\nvoid fitCircle(const std::vector<cv::Point2d>& points,\r\n               double& centerX, double& centerY, double& radius);\r\n\r\nstd::vector<cv::Point2d> intersectCircles(double x1, double y1, double r1,\r\n                                          double x2, double y2, double r2);\r\n\r\nvoid LLtoUTM(double latitude, double longitude,\r\n             double& utmNorthing, double& utmEasting,\r\n             std::string& utmZone);\r\nvoid UTMtoLL(double utmNorthing, double utmEasting,\r\n             const std::string& utmZone,\r\n             double& latitude, double& longitude);\r\n\r\nlong int timestampDiff(uint64_t t1, uint64_t t2);\r\n\r\n}\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/utils/camodocal/include/camodocal/sparse_graph/Transform.h",
    "content": "#ifndef TRANSFORM_H\n#define TRANSFORM_H\n\n#include <boost/shared_ptr.hpp>\n#include <eigen3/Eigen/Dense>\n#include <stdint.h>\n\nnamespace camodocal\n{\n\nclass Transform\n{\npublic:\n    EIGEN_MAKE_ALIGNED_OPERATOR_NEW\n\n    Transform();\n    Transform(const Eigen::Matrix4d& H);\n\n    Eigen::Quaterniond& rotation(void);\n    const Eigen::Quaterniond& rotation(void) const;\n    double* rotationData(void);\n    const double* const rotationData(void) const;\n\n    Eigen::Vector3d& translation(void);\n    const Eigen::Vector3d& translation(void) const;\n    double* translationData(void);\n    const double* const translationData(void) const;\n\n    Eigen::Matrix4d toMatrix(void) const;\n\nprivate:\n    Eigen::Quaterniond m_q;\n    Eigen::Vector3d m_t;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "src/utils/camodocal/src/calib/CameraCalibration.cc",
    "content": "#include \"camodocal/calib/CameraCalibration.h\"\n\n#include <cstdio>\n#include <eigen3/Eigen/Dense>\n#include <iomanip>\n#include <iostream>\n#include <algorithm>\n#include <fstream>\n#include <opencv2/core/core.hpp>\n#include <opencv2/core/eigen.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n#include <opencv2/calib3d/calib3d.hpp>\n\n#include \"camodocal/camera_models/CameraFactory.h\"\n#include \"camodocal/sparse_graph/Transform.h\"\n#include \"camodocal/gpl/EigenQuaternionParameterization.h\"\n#include \"camodocal/gpl/EigenUtils.h\"\n#include \"camodocal/camera_models/CostFunctionFactory.h\"\n\n#include \"ceres/ceres.h\"\nnamespace camodocal\n{\n\nCameraCalibration::CameraCalibration()\n : m_boardSize(cv::Size(0,0))\n , m_squareSize(0.0f)\n , m_verbose(false)\n{\n\n}\n\nCameraCalibration::CameraCalibration(const Camera::ModelType modelType,\n                                     const std::string& cameraName,\n                                     const cv::Size& imageSize,\n                                     const cv::Size& boardSize,\n                                     float squareSize)\n : m_boardSize(boardSize)\n , m_squareSize(squareSize)\n , m_verbose(false)\n{\n    m_camera = CameraFactory::instance()->generateCamera(modelType, cameraName, imageSize);\n}\n\nvoid\nCameraCalibration::clear(void)\n{\n    m_imagePoints.clear();\n    m_scenePoints.clear();\n}\n\nvoid\nCameraCalibration::addChessboardData(const std::vector<cv::Point2f>& corners)\n{\n    m_imagePoints.push_back(corners);\n\n    std::vector<cv::Point3f> scenePointsInView;\n    for (int i = 0; i < m_boardSize.height; ++i)\n    {\n        for (int j = 0; j < m_boardSize.width; ++j)\n        {\n            scenePointsInView.push_back(cv::Point3f(i * m_squareSize, j * m_squareSize, 0.0));\n        }\n    }\n    m_scenePoints.push_back(scenePointsInView);\n}\n\nbool\nCameraCalibration::calibrate(void)\n{\n    int imageCount = m_imagePoints.size();\n\n    // compute intrinsic camera parameters and extrinsic parameters for each of the views\n    std::vector<cv::Mat> rvecs;\n    std::vector<cv::Mat> tvecs;\n    bool ret = calibrateHelper(m_camera, rvecs, tvecs);\n\n    m_cameraPoses = cv::Mat(imageCount, 6, CV_64F);\n    for (int i = 0; i < imageCount; ++i)\n    {\n        m_cameraPoses.at<double>(i,0) = rvecs.at(i).at<double>(0);\n        m_cameraPoses.at<double>(i,1) = rvecs.at(i).at<double>(1);\n        m_cameraPoses.at<double>(i,2) = rvecs.at(i).at<double>(2);\n        m_cameraPoses.at<double>(i,3) = tvecs.at(i).at<double>(0);\n        m_cameraPoses.at<double>(i,4) = tvecs.at(i).at<double>(1);\n        m_cameraPoses.at<double>(i,5) = tvecs.at(i).at<double>(2);\n    }\n\n    // Compute measurement covariance.\n    std::vector<std::vector<cv::Point2f> > errVec(m_imagePoints.size());\n    Eigen::Vector2d errSum = Eigen::Vector2d::Zero();\n    size_t errCount = 0;\n    for (size_t i = 0; i < m_imagePoints.size(); ++i)\n    {\n        std::vector<cv::Point2f> estImagePoints;\n        m_camera->projectPoints(m_scenePoints.at(i), rvecs.at(i), tvecs.at(i),\n                                estImagePoints);\n\n        for (size_t j = 0; j < m_imagePoints.at(i).size(); ++j)\n        {\n            cv::Point2f pObs = m_imagePoints.at(i).at(j);\n            cv::Point2f pEst = estImagePoints.at(j);\n\n            cv::Point2f err = pObs - pEst;\n\n            errVec.at(i).push_back(err);\n\n            errSum += Eigen::Vector2d(err.x, err.y);\n        }\n\n        errCount += m_imagePoints.at(i).size();\n    }\n\n    Eigen::Vector2d errMean = errSum / static_cast<double>(errCount);\n\n    Eigen::Matrix2d measurementCovariance = Eigen::Matrix2d::Zero();\n    for (size_t i = 0; i < errVec.size(); ++i)\n    {\n        for (size_t j = 0; j < errVec.at(i).size(); ++j)\n        {\n            cv::Point2f err = errVec.at(i).at(j);\n            double d0 = err.x - errMean(0);\n            double d1 = err.y - errMean(1);\n\n            measurementCovariance(0,0) += d0 * d0;\n            measurementCovariance(0,1) += d0 * d1;\n            measurementCovariance(1,1) += d1 * d1;\n        }\n    }\n    measurementCovariance /= static_cast<double>(errCount);\n    measurementCovariance(1,0) = measurementCovariance(0,1);\n\n    m_measurementCovariance = measurementCovariance;\n\n    return ret;\n}\n\nint\nCameraCalibration::sampleCount(void) const\n{\n    return m_imagePoints.size();\n}\n\nstd::vector<std::vector<cv::Point2f> >&\nCameraCalibration::imagePoints(void)\n{\n    return m_imagePoints;\n}\n\nconst std::vector<std::vector<cv::Point2f> >&\nCameraCalibration::imagePoints(void) const\n{\n    return m_imagePoints;\n}\n\nstd::vector<std::vector<cv::Point3f> >&\nCameraCalibration::scenePoints(void)\n{\n    return m_scenePoints;\n}\n\nconst std::vector<std::vector<cv::Point3f> >&\nCameraCalibration::scenePoints(void) const\n{\n    return m_scenePoints;\n}\n\nCameraPtr&\nCameraCalibration::camera(void)\n{\n    return m_camera;\n}\n\nconst CameraConstPtr\nCameraCalibration::camera(void) const\n{\n    return m_camera;\n}\n\nEigen::Matrix2d&\nCameraCalibration::measurementCovariance(void)\n{\n    return m_measurementCovariance;\n}\n\nconst Eigen::Matrix2d&\nCameraCalibration::measurementCovariance(void) const\n{\n    return m_measurementCovariance;\n}\n\ncv::Mat&\nCameraCalibration::cameraPoses(void)\n{\n    return m_cameraPoses;\n}\n\nconst cv::Mat&\nCameraCalibration::cameraPoses(void) const\n{\n    return m_cameraPoses;\n}\n\nvoid\nCameraCalibration::drawResults(std::vector<cv::Mat>& images) const\n{\n    std::vector<cv::Mat> rvecs, tvecs;\n\n    for (size_t i = 0; i < images.size(); ++i)\n    {\n        cv::Mat rvec(3, 1, CV_64F);\n        rvec.at<double>(0) = m_cameraPoses.at<double>(i,0);\n        rvec.at<double>(1) = m_cameraPoses.at<double>(i,1);\n        rvec.at<double>(2) = m_cameraPoses.at<double>(i,2);\n\n        cv::Mat tvec(3, 1, CV_64F);\n        tvec.at<double>(0) = m_cameraPoses.at<double>(i,3);\n        tvec.at<double>(1) = m_cameraPoses.at<double>(i,4);\n        tvec.at<double>(2) = m_cameraPoses.at<double>(i,5);\n\n        rvecs.push_back(rvec);\n        tvecs.push_back(tvec);\n    }\n\n    int drawShiftBits = 4;\n    int drawMultiplier = 1 << drawShiftBits;\n\n    cv::Scalar green(0, 255, 0);\n    cv::Scalar red(0, 0, 255);\n\n    for (size_t i = 0; i < images.size(); ++i)\n    {\n        cv::Mat& image = images.at(i);\n        if (image.channels() == 1)\n        {\n            cv::cvtColor(image, image, CV_GRAY2RGB);\n        }\n\n        std::vector<cv::Point2f> estImagePoints;\n        m_camera->projectPoints(m_scenePoints.at(i), rvecs.at(i), tvecs.at(i),\n                                estImagePoints);\n\n        float errorSum = 0.0f;\n        float errorMax = std::numeric_limits<float>::min();\n\n        for (size_t j = 0; j < m_imagePoints.at(i).size(); ++j)\n        {\n            cv::Point2f pObs = m_imagePoints.at(i).at(j);\n            cv::Point2f pEst = estImagePoints.at(j);\n\n            cv::circle(image,\n                       cv::Point(cvRound(pObs.x * drawMultiplier),\n                                 cvRound(pObs.y * drawMultiplier)),\n                       5, green, 2, CV_AA, drawShiftBits);\n\n            cv::circle(image,\n                       cv::Point(cvRound(pEst.x * drawMultiplier),\n                                 cvRound(pEst.y * drawMultiplier)),\n                       5, red, 2, CV_AA, drawShiftBits);\n\n            float error = cv::norm(pObs - pEst);\n\n            errorSum += error;\n            if (error > errorMax)\n            {\n                errorMax = error;\n            }\n        }\n\n        std::ostringstream oss;\n        oss << \"Reprojection error: avg = \" << errorSum / m_imagePoints.at(i).size()\n            << \"   max = \" << errorMax;\n\n        cv::putText(image, oss.str(), cv::Point(10, image.rows - 10),\n                    cv::FONT_HERSHEY_COMPLEX, 0.5, cv::Scalar(255, 255, 255),\n                    1, CV_AA);\n    }\n}\n\nvoid\nCameraCalibration::writeParams(const std::string& filename) const\n{\n    m_camera->writeParametersToYamlFile(filename);\n}\n\nbool\nCameraCalibration::writeChessboardData(const std::string& filename) const\n{\n    std::ofstream ofs(filename.c_str(), std::ios::out | std::ios::binary);\n    if (!ofs.is_open())\n    {\n        return false;\n    }\n\n    writeData(ofs, m_boardSize.width);\n    writeData(ofs, m_boardSize.height);\n    writeData(ofs, m_squareSize);\n\n    writeData(ofs, m_measurementCovariance(0,0));\n    writeData(ofs, m_measurementCovariance(0,1));\n    writeData(ofs, m_measurementCovariance(1,0));\n    writeData(ofs, m_measurementCovariance(1,1));\n\n    writeData(ofs, m_cameraPoses.rows);\n    writeData(ofs, m_cameraPoses.cols);\n    writeData(ofs, m_cameraPoses.type());\n    for (int i = 0; i < m_cameraPoses.rows; ++i)\n    {\n        for (int j = 0; j < m_cameraPoses.cols; ++j)\n        {\n            writeData(ofs, m_cameraPoses.at<double>(i,j));\n        }\n    }\n\n    writeData(ofs, m_imagePoints.size());\n    for (size_t i = 0; i < m_imagePoints.size(); ++i)\n    {\n        writeData(ofs, m_imagePoints.at(i).size());\n        for (size_t j = 0; j < m_imagePoints.at(i).size(); ++j)\n        {\n            const cv::Point2f& ipt = m_imagePoints.at(i).at(j);\n\n            writeData(ofs, ipt.x);\n            writeData(ofs, ipt.y);\n        }\n    }\n\n    writeData(ofs, m_scenePoints.size());\n    for (size_t i = 0; i < m_scenePoints.size(); ++i)\n    {\n        writeData(ofs, m_scenePoints.at(i).size());\n        for (size_t j = 0; j < m_scenePoints.at(i).size(); ++j)\n        {\n            const cv::Point3f& spt = m_scenePoints.at(i).at(j);\n\n            writeData(ofs, spt.x);\n            writeData(ofs, spt.y);\n            writeData(ofs, spt.z);\n        }\n    }\n\n    return true;\n}\n\nbool\nCameraCalibration::readChessboardData(const std::string& filename)\n{\n    std::ifstream ifs(filename.c_str(), std::ios::in | std::ios::binary);\n    if (!ifs.is_open())\n    {\n        return false;\n    }\n\n    readData(ifs, m_boardSize.width);\n    readData(ifs, m_boardSize.height);\n    readData(ifs, m_squareSize);\n\n    readData(ifs, m_measurementCovariance(0,0));\n    readData(ifs, m_measurementCovariance(0,1));\n    readData(ifs, m_measurementCovariance(1,0));\n    readData(ifs, m_measurementCovariance(1,1));\n\n    int rows, cols, type;\n    readData(ifs, rows);\n    readData(ifs, cols);\n    readData(ifs, type);\n    m_cameraPoses = cv::Mat(rows, cols, type);\n\n    for (int i = 0; i < m_cameraPoses.rows; ++i)\n    {\n        for (int j = 0; j < m_cameraPoses.cols; ++j)\n        {\n            readData(ifs, m_cameraPoses.at<double>(i,j));\n        }\n    }\n\n    size_t nImagePointSets;\n    readData(ifs, nImagePointSets);\n\n    m_imagePoints.clear();\n    m_imagePoints.resize(nImagePointSets);\n    for (size_t i = 0; i < m_imagePoints.size(); ++i)\n    {\n        size_t nImagePoints;\n        readData(ifs, nImagePoints);\n        m_imagePoints.at(i).resize(nImagePoints);\n\n        for (size_t j = 0; j < m_imagePoints.at(i).size(); ++j)\n        {\n            cv::Point2f& ipt = m_imagePoints.at(i).at(j);\n            readData(ifs, ipt.x);\n            readData(ifs, ipt.y);\n        }\n    }\n\n    size_t nScenePointSets;\n    readData(ifs, nScenePointSets);\n\n    m_scenePoints.clear();\n    m_scenePoints.resize(nScenePointSets);\n    for (size_t i = 0; i < m_scenePoints.size(); ++i)\n    {\n        size_t nScenePoints;\n        readData(ifs, nScenePoints);\n        m_scenePoints.at(i).resize(nScenePoints);\n\n        for (size_t j = 0; j < m_scenePoints.at(i).size(); ++j)\n        {\n            cv::Point3f& spt = m_scenePoints.at(i).at(j);\n            readData(ifs, spt.x);\n            readData(ifs, spt.y);\n            readData(ifs, spt.z);\n        }\n    }\n\n    return true;\n}\n\nvoid\nCameraCalibration::setVerbose(bool verbose)\n{\n    m_verbose = verbose;\n}\n\nbool\nCameraCalibration::calibrateHelper(CameraPtr& camera,\n                                   std::vector<cv::Mat>& rvecs, std::vector<cv::Mat>& tvecs) const\n{\n    rvecs.assign(m_scenePoints.size(), cv::Mat());\n    tvecs.assign(m_scenePoints.size(), cv::Mat());\n\n    // STEP 1: Estimate intrinsics\n    camera->estimateIntrinsics(m_boardSize, m_scenePoints, m_imagePoints);\n\n    // STEP 2: Estimate extrinsics\n    for (size_t i = 0; i < m_scenePoints.size(); ++i)\n    {\n        camera->estimateExtrinsics(m_scenePoints.at(i), m_imagePoints.at(i), rvecs.at(i), tvecs.at(i));\n    }\n\n    if (m_verbose)\n    {\n        std::cout << \"[\" << camera->cameraName() << \"] \"\n                  << \"# INFO: \" << \"Initial reprojection error: \"\n                  << std::fixed << std::setprecision(3)\n                  << camera->reprojectionError(m_scenePoints, m_imagePoints, rvecs, tvecs)\n                  << \" pixels\" << std::endl;\n    }\n\n    // STEP 3: optimization using ceres\n    optimize(camera, rvecs, tvecs);\n\n    if (m_verbose)\n    {\n        double err = camera->reprojectionError(m_scenePoints, m_imagePoints, rvecs, tvecs);\n        std::cout << \"[\" << camera->cameraName() << \"] \" << \"# INFO: Final reprojection error: \"\n                  << err << \" pixels\" << std::endl;\n        std::cout << \"[\" << camera->cameraName() << \"] \" << \"# INFO: \"\n                  << camera->parametersToString() << std::endl;\n    }\n\n    return true;\n}\n\nvoid\nCameraCalibration::optimize(CameraPtr& camera,\n                            std::vector<cv::Mat>& rvecs, std::vector<cv::Mat>& tvecs) const\n{\n    // Use ceres to do optimization\n    ceres::Problem problem;\n\n    std::vector<Transform, Eigen::aligned_allocator<Transform> > transformVec(rvecs.size());\n    for (size_t i = 0; i < rvecs.size(); ++i)\n    {\n        Eigen::Vector3d rvec;\n        cv::cv2eigen(rvecs.at(i), rvec);\n\n        transformVec.at(i).rotation() = Eigen::AngleAxisd(rvec.norm(), rvec.normalized());\n        transformVec.at(i).translation() << tvecs[i].at<double>(0),\n                                            tvecs[i].at<double>(1),\n                                            tvecs[i].at<double>(2);\n    }\n\n    std::vector<double> intrinsicCameraParams;\n    m_camera->writeParameters(intrinsicCameraParams);\n\n    // create residuals for each observation\n    for (size_t i = 0; i < m_imagePoints.size(); ++i)\n    {\n        for (size_t j = 0; j < m_imagePoints.at(i).size(); ++j)\n        {\n            const cv::Point3f& spt = m_scenePoints.at(i).at(j);\n            const cv::Point2f& ipt = m_imagePoints.at(i).at(j);\n\n            ceres::CostFunction* costFunction =\n                CostFunctionFactory::instance()->generateCostFunction(camera,\n                                                                      Eigen::Vector3d(spt.x, spt.y, spt.z),\n                                                                      Eigen::Vector2d(ipt.x, ipt.y),\n                                                                      CAMERA_INTRINSICS | CAMERA_POSE);\n\n            ceres::LossFunction* lossFunction = new ceres::CauchyLoss(1.0);\n            problem.AddResidualBlock(costFunction, lossFunction,\n                                     intrinsicCameraParams.data(),\n                                     transformVec.at(i).rotationData(),\n                                     transformVec.at(i).translationData());\n        }\n\n        ceres::LocalParameterization* quaternionParameterization =\n            new EigenQuaternionParameterization;\n\n        problem.SetParameterization(transformVec.at(i).rotationData(),\n                                    quaternionParameterization);\n    }\n\n    std::cout << \"begin ceres\" << std::endl;\n    ceres::Solver::Options options;\n    options.max_num_iterations = 1000;\n    options.num_threads = 1;\n\n    if (m_verbose)\n    {\n        options.minimizer_progress_to_stdout = true;\n    }\n\n    ceres::Solver::Summary summary;\n    ceres::Solve(options, &problem, &summary);\n    std::cout << \"end ceres\" << std::endl;\n\n    if (m_verbose)\n    {\n        std::cout << summary.FullReport() << std::endl;\n    }\n\n    camera->readParameters(intrinsicCameraParams);\n\n    for (size_t i = 0; i < rvecs.size(); ++i)\n    {\n        Eigen::AngleAxisd aa(transformVec.at(i).rotation());\n\n        Eigen::Vector3d rvec = aa.angle() * aa.axis();\n        cv::eigen2cv(rvec, rvecs.at(i));\n\n        cv::Mat& tvec = tvecs.at(i);\n        tvec.at<double>(0) = transformVec.at(i).translation()(0);\n        tvec.at<double>(1) = transformVec.at(i).translation()(1);\n        tvec.at<double>(2) = transformVec.at(i).translation()(2);\n    }\n}\n\ntemplate<typename T>\nvoid\nCameraCalibration::readData(std::ifstream& ifs, T& data) const\n{\n    char* buffer = new char[sizeof(T)];\n\n    ifs.read(buffer, sizeof(T));\n\n    data = *(reinterpret_cast<T*>(buffer));\n\n    delete[] buffer;\n}\n\ntemplate<typename T>\nvoid\nCameraCalibration::writeData(std::ofstream& ofs, T data) const\n{\n    char* pData = reinterpret_cast<char*>(&data);\n\n    ofs.write(pData, sizeof(T));\n}\n\n}\n"
  },
  {
    "path": "src/utils/camodocal/src/camera_models/Camera.cc",
    "content": "#include \"camodocal/camera_models/Camera.h\"\n#include \"camodocal/camera_models/ScaramuzzaCamera.h\"\n\n#include <opencv2/calib3d/calib3d.hpp>\n\nnamespace camodocal\n{\n\nCamera::Parameters::Parameters(ModelType modelType)\n : m_modelType(modelType)\n , m_imageWidth(0)\n , m_imageHeight(0)\n{\n    switch (modelType)\n    {\n    case KANNALA_BRANDT:\n        m_nIntrinsics = 8;\n        break;\n    case PINHOLE:\n        m_nIntrinsics = 8;\n        break;\n    case SCARAMUZZA:\n        m_nIntrinsics = SCARAMUZZA_CAMERA_NUM_PARAMS;\n        break;\n    case MEI:\n    default:\n        m_nIntrinsics = 9;\n    }\n}\n\nCamera::Parameters::Parameters(ModelType modelType,\n                               const std::string& cameraName,\n                               int w, int h)\n : m_modelType(modelType)\n , m_cameraName(cameraName)\n , m_imageWidth(w)\n , m_imageHeight(h)\n{\n    switch (modelType)\n    {\n    case KANNALA_BRANDT:\n        m_nIntrinsics = 8;\n        break;\n    case PINHOLE:\n        m_nIntrinsics = 8;\n        break;\n    case SCARAMUZZA:\n        m_nIntrinsics = SCARAMUZZA_CAMERA_NUM_PARAMS;\n        break;\n    case MEI:\n    default:\n        m_nIntrinsics = 9;\n    }\n}\n\nCamera::ModelType&\nCamera::Parameters::modelType(void)\n{\n    return m_modelType;\n}\n\nstd::string&\nCamera::Parameters::cameraName(void)\n{\n    return m_cameraName;\n}\n\nint&\nCamera::Parameters::imageWidth(void)\n{\n    return m_imageWidth;\n}\n\nint&\nCamera::Parameters::imageHeight(void)\n{\n    return m_imageHeight;\n}\n\nCamera::ModelType\nCamera::Parameters::modelType(void) const\n{\n    return m_modelType;\n}\n\nconst std::string&\nCamera::Parameters::cameraName(void) const\n{\n    return m_cameraName;\n}\n\nint\nCamera::Parameters::imageWidth(void) const\n{\n    return m_imageWidth;\n}\n\nint\nCamera::Parameters::imageHeight(void) const\n{\n    return m_imageHeight;\n}\n\nint\nCamera::Parameters::nIntrinsics(void) const\n{\n    return m_nIntrinsics;\n}\n\ncv::Mat&\nCamera::mask(void)\n{\n    return m_mask;\n}\n\nconst cv::Mat&\nCamera::mask(void) const\n{\n    return m_mask;\n}\n\nvoid\nCamera::estimateExtrinsics(const std::vector<cv::Point3f>& objectPoints,\n                           const std::vector<cv::Point2f>& imagePoints,\n                           cv::Mat& rvec, cv::Mat& tvec) const\n{\n    std::vector<cv::Point2f> Ms(imagePoints.size());\n    for (size_t i = 0; i < Ms.size(); ++i)\n    {\n        Eigen::Vector3d P;\n        liftProjective(Eigen::Vector2d(imagePoints.at(i).x, imagePoints.at(i).y), P);\n\n        P /= P(2);\n\n        Ms.at(i).x = P(0);\n        Ms.at(i).y = P(1);\n    }\n\n    // assume unit focal length, zero principal point, and zero distortion\n    cv::solvePnP(objectPoints, Ms, cv::Mat::eye(3, 3, CV_64F), cv::noArray(), rvec, tvec);\n}\n\ndouble\nCamera::reprojectionDist(const Eigen::Vector3d& P1, const Eigen::Vector3d& P2) const\n{\n    Eigen::Vector2d p1, p2;\n\n    spaceToPlane(P1, p1);\n    spaceToPlane(P2, p2);\n\n    return (p1 - p2).norm();\n}\n\ndouble\nCamera::reprojectionError(const std::vector< std::vector<cv::Point3f> >& objectPoints,\n                          const std::vector< std::vector<cv::Point2f> >& imagePoints,\n                          const std::vector<cv::Mat>& rvecs,\n                          const std::vector<cv::Mat>& tvecs,\n                          cv::OutputArray _perViewErrors) const\n{\n    int imageCount = objectPoints.size();\n    size_t pointsSoFar = 0;\n    double totalErr = 0.0;\n\n    bool computePerViewErrors = _perViewErrors.needed();\n    cv::Mat perViewErrors;\n    if (computePerViewErrors)\n    {\n        _perViewErrors.create(imageCount, 1, CV_64F);\n        perViewErrors = _perViewErrors.getMat();\n    }\n\n    for (int i = 0; i < imageCount; ++i)\n    {\n        size_t pointCount = imagePoints.at(i).size();\n\n        pointsSoFar += pointCount;\n\n        std::vector<cv::Point2f> estImagePoints;\n        projectPoints(objectPoints.at(i), rvecs.at(i), tvecs.at(i),\n                      estImagePoints);\n\n        double err = 0.0;\n        for (size_t j = 0; j < imagePoints.at(i).size(); ++j)\n        {\n            err += cv::norm(imagePoints.at(i).at(j) - estImagePoints.at(j));\n        }\n\n        if (computePerViewErrors)\n        {\n            perViewErrors.at<double>(i) = err / pointCount;\n        }\n\n        totalErr += err;\n    }\n\n    return totalErr / pointsSoFar;\n}\n\ndouble\nCamera::reprojectionError(const Eigen::Vector3d& P,\n                          const Eigen::Quaterniond& camera_q,\n                          const Eigen::Vector3d& camera_t,\n                          const Eigen::Vector2d& observed_p) const\n{\n    Eigen::Vector3d P_cam = camera_q.toRotationMatrix() * P + camera_t;\n\n    Eigen::Vector2d p;\n    spaceToPlane(P_cam, p);\n\n    return (p - observed_p).norm();\n}\n\nvoid\nCamera::projectPoints(const std::vector<cv::Point3f>& objectPoints,\n                      const cv::Mat& rvec,\n                      const cv::Mat& tvec,\n                      std::vector<cv::Point2f>& imagePoints) const\n{\n    // project 3D object points to the image plane\n    imagePoints.reserve(objectPoints.size());\n\n    //double\n    cv::Mat R0;\n    cv::Rodrigues(rvec, R0);\n\n    Eigen::MatrixXd R(3,3);\n    R << R0.at<double>(0,0), R0.at<double>(0,1), R0.at<double>(0,2),\n         R0.at<double>(1,0), R0.at<double>(1,1), R0.at<double>(1,2),\n         R0.at<double>(2,0), R0.at<double>(2,1), R0.at<double>(2,2);\n\n    Eigen::Vector3d t;\n    t << tvec.at<double>(0), tvec.at<double>(1), tvec.at<double>(2);\n\n    for (size_t i = 0; i < objectPoints.size(); ++i)\n    {\n        const cv::Point3f& objectPoint = objectPoints.at(i);\n\n        // Rotate and translate\n        Eigen::Vector3d P;\n        P << objectPoint.x, objectPoint.y, objectPoint.z;\n\n        P = R * P + t;\n\n        Eigen::Vector2d p;\n        spaceToPlane(P, p);\n\n        imagePoints.push_back(cv::Point2f(p(0), p(1)));\n    }\n}\n\n}\n"
  },
  {
    "path": "src/utils/camodocal/src/camera_models/CameraFactory.cc",
    "content": "#include \"camodocal/camera_models/CameraFactory.h\"\n\n#include <boost/algorithm/string.hpp>\n\n\n#include \"camodocal/camera_models/CataCamera.h\"\n#include \"camodocal/camera_models/EquidistantCamera.h\"\n#include \"camodocal/camera_models/PinholeCamera.h\"\n#include \"camodocal/camera_models/ScaramuzzaCamera.h\"\n\n#include \"ceres/ceres.h\"\n\nnamespace camodocal\n{\n\nboost::shared_ptr<CameraFactory> CameraFactory::m_instance;\n\nCameraFactory::CameraFactory()\n{\n\n}\n\nboost::shared_ptr<CameraFactory>\nCameraFactory::instance(void)\n{\n    if (m_instance.get() == 0)\n    {\n        m_instance.reset(new CameraFactory);\n    }\n\n    return m_instance;\n}\n\nCameraPtr\nCameraFactory::generateCamera(Camera::ModelType modelType,\n                              const std::string& cameraName,\n                              cv::Size imageSize) const\n{\n    switch (modelType)\n    {\n    case Camera::KANNALA_BRANDT:\n    {\n        EquidistantCameraPtr camera(new EquidistantCamera);\n\n        EquidistantCamera::Parameters params = camera->getParameters();\n        params.cameraName() = cameraName;\n        params.imageWidth() = imageSize.width;\n        params.imageHeight() = imageSize.height;\n        camera->setParameters(params);\n        return camera;\n    }\n    case Camera::PINHOLE:\n    {\n        PinholeCameraPtr camera(new PinholeCamera);\n\n        PinholeCamera::Parameters params = camera->getParameters();\n        params.cameraName() = cameraName;\n        params.imageWidth() = imageSize.width;\n        params.imageHeight() = imageSize.height;\n        camera->setParameters(params);\n        return camera;\n    }\n    case Camera::SCARAMUZZA:\n    {\n        OCAMCameraPtr camera(new OCAMCamera);\n\n        OCAMCamera::Parameters params = camera->getParameters();\n        params.cameraName() = cameraName;\n        params.imageWidth() = imageSize.width;\n        params.imageHeight() = imageSize.height;\n        camera->setParameters(params);\n        return camera;\n    }\n    case Camera::MEI:\n    default:\n    {\n        CataCameraPtr camera(new CataCamera);\n\n        CataCamera::Parameters params = camera->getParameters();\n        params.cameraName() = cameraName;\n        params.imageWidth() = imageSize.width;\n        params.imageHeight() = imageSize.height;\n        camera->setParameters(params);\n        return camera;\n    }\n    }\n}\n\nCameraPtr\nCameraFactory::generateCameraFromYamlFile(const std::string& filename)\n{\n    cv::FileStorage fs(filename, cv::FileStorage::READ);\n\n    if (!fs.isOpened())\n    {\n        return CameraPtr();\n    }\n\n    Camera::ModelType modelType = Camera::MEI;\n    if (!fs[\"model_type\"].isNone())\n    {\n        std::string sModelType;\n        fs[\"model_type\"] >> sModelType;\n\n        if (boost::iequals(sModelType, \"kannala_brandt\"))\n        {\n            modelType = Camera::KANNALA_BRANDT;\n        }\n        else if (boost::iequals(sModelType, \"mei\"))\n        {\n            modelType = Camera::MEI;\n        }\n        else if (boost::iequals(sModelType, \"scaramuzza\"))\n        {\n            modelType = Camera::SCARAMUZZA;\n        }\n        else if (boost::iequals(sModelType, \"pinhole\"))\n        {\n            modelType = Camera::PINHOLE;\n        }\n        else\n        {\n            std::cerr << \"# ERROR: Unknown camera model: \" << sModelType << std::endl;\n            return CameraPtr();\n        }\n    }\n\n    switch (modelType)\n    {\n    case Camera::KANNALA_BRANDT:\n    {\n        EquidistantCameraPtr camera(new EquidistantCamera);\n\n        EquidistantCamera::Parameters params = camera->getParameters();\n        params.readFromYamlFile(filename);\n        camera->setParameters(params);\n        return camera;\n    }\n    case Camera::PINHOLE:\n    {\n        PinholeCameraPtr camera(new PinholeCamera);\n\n        PinholeCamera::Parameters params = camera->getParameters();\n        params.readFromYamlFile(filename);\n        camera->setParameters(params);\n        return camera;\n    }\n    case Camera::SCARAMUZZA:\n    {\n        OCAMCameraPtr camera(new OCAMCamera);\n\n        OCAMCamera::Parameters params = camera->getParameters();\n        params.readFromYamlFile(filename);\n        camera->setParameters(params);\n        return camera;\n    }\n    case Camera::MEI:\n    default:\n    {\n        CataCameraPtr camera(new CataCamera);\n\n        CataCamera::Parameters params = camera->getParameters();\n        params.readFromYamlFile(filename);\n        camera->setParameters(params);\n        return camera;\n    }\n    }\n\n    return CameraPtr();\n}\n\n}\n\n"
  },
  {
    "path": "src/utils/camodocal/src/camera_models/CataCamera.cc",
    "content": "#include \"camodocal/camera_models/CataCamera.h\"\n\n#include <cmath>\n#include <cstdio>\n#include <eigen3/Eigen/Dense>\n#include <iomanip>\n#include <iostream>\n#include <opencv2/calib3d/calib3d.hpp>\n#include <opencv2/core/eigen.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n#include \"camodocal/gpl/gpl.h\"\n\nnamespace camodocal\n{\n\nCataCamera::Parameters::Parameters()\n : Camera::Parameters(MEI)\n , m_xi(0.0)\n , m_k1(0.0)\n , m_k2(0.0)\n , m_p1(0.0)\n , m_p2(0.0)\n , m_gamma1(0.0)\n , m_gamma2(0.0)\n , m_u0(0.0)\n , m_v0(0.0)\n{\n\n}\n\nCataCamera::Parameters::Parameters(const std::string& cameraName,\n                                   int w, int h,\n                                   double xi,\n                                   double k1, double k2,\n                                   double p1, double p2,\n                                   double gamma1, double gamma2,\n                                   double u0, double v0)\n : Camera::Parameters(MEI, cameraName, w, h)\n , m_xi(xi)\n , m_k1(k1)\n , m_k2(k2)\n , m_p1(p1)\n , m_p2(p2)\n , m_gamma1(gamma1)\n , m_gamma2(gamma2)\n , m_u0(u0)\n , m_v0(v0)\n{\n}\n\ndouble&\nCataCamera::Parameters::xi(void)\n{\n    return m_xi;\n}\n\ndouble&\nCataCamera::Parameters::k1(void)\n{\n    return m_k1;\n}\n\ndouble&\nCataCamera::Parameters::k2(void)\n{\n    return m_k2;\n}\n\ndouble&\nCataCamera::Parameters::p1(void)\n{\n    return m_p1;\n}\n\ndouble&\nCataCamera::Parameters::p2(void)\n{\n    return m_p2;\n}\n\ndouble&\nCataCamera::Parameters::gamma1(void)\n{\n    return m_gamma1;\n}\n\ndouble&\nCataCamera::Parameters::gamma2(void)\n{\n    return m_gamma2;\n}\n\ndouble&\nCataCamera::Parameters::u0(void)\n{\n    return m_u0;\n}\n\ndouble&\nCataCamera::Parameters::v0(void)\n{\n    return m_v0;\n}\n\ndouble\nCataCamera::Parameters::xi(void) const\n{\n    return m_xi;\n}\n\ndouble\nCataCamera::Parameters::k1(void) const\n{\n    return m_k1;\n}\n\ndouble\nCataCamera::Parameters::k2(void) const\n{\n    return m_k2;\n}\n\ndouble\nCataCamera::Parameters::p1(void) const\n{\n    return m_p1;\n}\n\ndouble\nCataCamera::Parameters::p2(void) const\n{\n    return m_p2;\n}\n\ndouble\nCataCamera::Parameters::gamma1(void) const\n{\n    return m_gamma1;\n}\n\ndouble\nCataCamera::Parameters::gamma2(void) const\n{\n    return m_gamma2;\n}\n\ndouble\nCataCamera::Parameters::u0(void) const\n{\n    return m_u0;\n}\n\ndouble\nCataCamera::Parameters::v0(void) const\n{\n    return m_v0;\n}\n\nbool\nCataCamera::Parameters::readFromYamlFile(const std::string& filename)\n{\n    cv::FileStorage fs(filename, cv::FileStorage::READ);\n\n    if (!fs.isOpened())\n    {\n        return false;\n    }\n\n    if (!fs[\"model_type\"].isNone())\n    {\n        std::string sModelType;\n        fs[\"model_type\"] >> sModelType;\n\n        if (sModelType.compare(\"MEI\") != 0)\n        {\n            return false;\n        }\n    }\n\n    m_modelType = MEI;\n    fs[\"camera_name\"] >> m_cameraName;\n    m_imageWidth = static_cast<int>(fs[\"image_width\"]);\n    m_imageHeight = static_cast<int>(fs[\"image_height\"]);\n\n    cv::FileNode n = fs[\"mirror_parameters\"];\n    m_xi = static_cast<double>(n[\"xi\"]);\n\n    n = fs[\"distortion_parameters\"];\n    m_k1 = static_cast<double>(n[\"k1\"]);\n    m_k2 = static_cast<double>(n[\"k2\"]);\n    m_p1 = static_cast<double>(n[\"p1\"]);\n    m_p2 = static_cast<double>(n[\"p2\"]);\n\n    n = fs[\"projection_parameters\"];\n    m_gamma1 = static_cast<double>(n[\"gamma1\"]);\n    m_gamma2 = static_cast<double>(n[\"gamma2\"]);\n    m_u0 = static_cast<double>(n[\"u0\"]);\n    m_v0 = static_cast<double>(n[\"v0\"]);\n\n    return true;\n}\n\nvoid\nCataCamera::Parameters::writeToYamlFile(const std::string& filename) const\n{\n    cv::FileStorage fs(filename, cv::FileStorage::WRITE);\n\n    fs << \"model_type\" << \"MEI\";\n    fs << \"camera_name\" << m_cameraName;\n    fs << \"image_width\" << m_imageWidth;\n    fs << \"image_height\" << m_imageHeight;\n\n    // mirror: xi\n    fs << \"mirror_parameters\";\n    fs << \"{\" << \"xi\" << m_xi << \"}\";\n\n    // radial distortion: k1, k2\n    // tangential distortion: p1, p2\n    fs << \"distortion_parameters\";\n    fs << \"{\" << \"k1\" << m_k1\n              << \"k2\" << m_k2\n              << \"p1\" << m_p1\n              << \"p2\" << m_p2 << \"}\";\n\n    // projection: gamma1, gamma2, u0, v0\n    fs << \"projection_parameters\";\n    fs << \"{\" << \"gamma1\" << m_gamma1\n              << \"gamma2\" << m_gamma2\n              << \"u0\" << m_u0\n              << \"v0\" << m_v0 << \"}\";\n\n    fs.release();\n}\n\nCataCamera::Parameters&\nCataCamera::Parameters::operator=(const CataCamera::Parameters& other)\n{\n    if (this != &other)\n    {\n        m_modelType = other.m_modelType;\n        m_cameraName = other.m_cameraName;\n        m_imageWidth = other.m_imageWidth;\n        m_imageHeight = other.m_imageHeight;\n        m_xi = other.m_xi;\n        m_k1 = other.m_k1;\n        m_k2 = other.m_k2;\n        m_p1 = other.m_p1;\n        m_p2 = other.m_p2;\n        m_gamma1 = other.m_gamma1;\n        m_gamma2 = other.m_gamma2;\n        m_u0 = other.m_u0;\n        m_v0 = other.m_v0;\n    }\n\n    return *this;\n}\n\nstd::ostream&\noperator<< (std::ostream& out, const CataCamera::Parameters& params)\n{\n    out << \"Camera Parameters:\" << std::endl;\n    out << \"    model_type \" << \"MEI\" << std::endl;\n    out << \"   camera_name \" << params.m_cameraName << std::endl;\n    out << \"   image_width \" << params.m_imageWidth << std::endl;\n    out << \"  image_height \" << params.m_imageHeight << std::endl;\n\n    out << \"Mirror Parameters\" << std::endl;\n    out << std::fixed << std::setprecision(10);\n    out << \"            xi \" << params.m_xi << std::endl;\n\n    // radial distortion: k1, k2\n    // tangential distortion: p1, p2\n    out << \"Distortion Parameters\" << std::endl;\n    out << \"            k1 \" << params.m_k1 << std::endl\n        << \"            k2 \" << params.m_k2 << std::endl\n        << \"            p1 \" << params.m_p1 << std::endl\n        << \"            p2 \" << params.m_p2 << std::endl;\n\n    // projection: gamma1, gamma2, u0, v0\n    out << \"Projection Parameters\" << std::endl;\n    out << \"        gamma1 \" << params.m_gamma1 << std::endl\n        << \"        gamma2 \" << params.m_gamma2 << std::endl\n        << \"            u0 \" << params.m_u0 << std::endl\n        << \"            v0 \" << params.m_v0 << std::endl;\n\n    return out;\n}\n\nCataCamera::CataCamera()\n : m_inv_K11(1.0)\n , m_inv_K13(0.0)\n , m_inv_K22(1.0)\n , m_inv_K23(0.0)\n , m_noDistortion(true)\n{\n\n}\n\nCataCamera::CataCamera(const std::string& cameraName,\n                       int imageWidth, int imageHeight,\n                       double xi, double k1, double k2, double p1, double p2,\n                       double gamma1, double gamma2, double u0, double v0)\n : mParameters(cameraName, imageWidth, imageHeight,\n               xi, k1, k2, p1, p2, gamma1, gamma2, u0, v0)\n{\n    if ((mParameters.k1() == 0.0) &&\n        (mParameters.k2() == 0.0) &&\n        (mParameters.p1() == 0.0) &&\n        (mParameters.p2() == 0.0))\n    {\n        m_noDistortion = true;\n    }\n    else\n    {\n        m_noDistortion = false;\n    }\n\n    // Inverse camera projection matrix parameters\n    m_inv_K11 = 1.0 / mParameters.gamma1();\n    m_inv_K13 = -mParameters.u0() / mParameters.gamma1();\n    m_inv_K22 = 1.0 / mParameters.gamma2();\n    m_inv_K23 = -mParameters.v0() / mParameters.gamma2();\n}\n\nCataCamera::CataCamera(const CataCamera::Parameters& params)\n : mParameters(params)\n{\n    if ((mParameters.k1() == 0.0) &&\n        (mParameters.k2() == 0.0) &&\n        (mParameters.p1() == 0.0) &&\n        (mParameters.p2() == 0.0))\n    {\n        m_noDistortion = true;\n    }\n    else\n    {\n        m_noDistortion = false;\n    }\n\n    // Inverse camera projection matrix parameters\n    m_inv_K11 = 1.0 / mParameters.gamma1();\n    m_inv_K13 = -mParameters.u0() / mParameters.gamma1();\n    m_inv_K22 = 1.0 / mParameters.gamma2();\n    m_inv_K23 = -mParameters.v0() / mParameters.gamma2();\n}\n\nCamera::ModelType\nCataCamera::modelType(void) const\n{\n    return mParameters.modelType();\n}\n\nconst std::string&\nCataCamera::cameraName(void) const\n{\n    return mParameters.cameraName();\n}\n\nint\nCataCamera::imageWidth(void) const\n{\n    return mParameters.imageWidth();\n}\n\nint\nCataCamera::imageHeight(void) const\n{\n    return mParameters.imageHeight();\n}\n\nvoid\nCataCamera::estimateIntrinsics(const cv::Size& boardSize,\n                               const std::vector< std::vector<cv::Point3f> >& objectPoints,\n                               const std::vector< std::vector<cv::Point2f> >& imagePoints)\n{\n    Parameters params = getParameters();\n\n    double u0 = params.imageWidth() / 2.0;\n    double v0 = params.imageHeight() / 2.0;\n\n    double gamma0 = 0.0;\n    double minReprojErr = std::numeric_limits<double>::max();\n\n    std::vector<cv::Mat> rvecs, tvecs;\n    rvecs.assign(objectPoints.size(), cv::Mat());\n    tvecs.assign(objectPoints.size(), cv::Mat());\n\n    params.xi() = 1.0;\n    params.k1() = 0.0;\n    params.k2() = 0.0;\n    params.p1() = 0.0;\n    params.p2() = 0.0;\n    params.u0() = u0;\n    params.v0() = v0;\n\n    // Initialize gamma (focal length)\n    // Use non-radial line image and xi = 1\n    for (size_t i = 0; i < imagePoints.size(); ++i)\n    {\n        for (int r = 0; r < boardSize.height; ++r)\n        {\n            cv::Mat P(boardSize.width, 4, CV_64F);\n            for (int c = 0; c < boardSize.width; ++c)\n            {\n                const cv::Point2f& imagePoint = imagePoints.at(i).at(r * boardSize.width + c);\n\n                double u = imagePoint.x - u0;\n                double v = imagePoint.y - v0;\n\n                P.at<double>(c, 0) = u;\n                P.at<double>(c, 1) = v;\n                P.at<double>(c, 2) = 0.5;\n                P.at<double>(c, 3) = -0.5 * (square(u) + square(v));\n            }\n\n            cv::Mat C;\n            cv::SVD::solveZ(P, C);\n\n            double t = square(C.at<double>(0)) + square(C.at<double>(1)) + C.at<double>(2) * C.at<double>(3);\n            if (t < 0.0)\n            {\n                continue;\n            }\n\n            // check that line image is not radial\n            double d = sqrt(1.0 / t);\n            double nx = C.at<double>(0) * d;\n            double ny = C.at<double>(1) * d;\n            if (hypot(nx, ny) > 0.95)\n            {\n                continue;\n            }\n\n            double gamma = sqrt(C.at<double>(2) / C.at<double>(3));\n\n            params.gamma1() = gamma;\n            params.gamma2() = gamma;\n            setParameters(params);\n\n            for (size_t j = 0; j < objectPoints.size(); ++j)\n            {\n                estimateExtrinsics(objectPoints.at(j), imagePoints.at(j), rvecs.at(j), tvecs.at(j));\n            }\n\n            double reprojErr = reprojectionError(objectPoints, imagePoints, rvecs, tvecs, cv::noArray());\n\n            if (reprojErr < minReprojErr)\n            {\n                minReprojErr = reprojErr;\n                gamma0 = gamma;\n            }\n        }\n    }\n\n    if (gamma0 <= 0.0 && minReprojErr >= std::numeric_limits<double>::max())\n    {\n        std::cout << \"[\" << params.cameraName() << \"] \"\n                  << \"# INFO: CataCamera model fails with given data. \" << std::endl;\n\n        return;\n    }\n\n    params.gamma1() = gamma0;\n    params.gamma2() = gamma0;\n    setParameters(params);\n}\n\n/** \n * \\brief Lifts a point from the image plane to the unit sphere\n *\n * \\param p image coordinates\n * \\param P coordinates of the point on the sphere\n */\nvoid\nCataCamera::liftSphere(const Eigen::Vector2d& p, Eigen::Vector3d& P) const\n{\n    double mx_d, my_d,mx2_d, mxy_d, my2_d, mx_u, my_u;\n    double rho2_d, rho4_d, radDist_d, Dx_d, Dy_d, inv_denom_d;\n    double lambda;\n\n    // Lift points to normalised plane\n    mx_d = m_inv_K11 * p(0) + m_inv_K13;\n    my_d = m_inv_K22 * p(1) + m_inv_K23;\n\n    if (m_noDistortion)\n    {\n        mx_u = mx_d;\n        my_u = my_d;\n    }\n    else\n    {\n        // Apply inverse distortion model\n        if (0)\n        {\n            double k1 = mParameters.k1();\n            double k2 = mParameters.k2();\n            double p1 = mParameters.p1();\n            double p2 = mParameters.p2();\n\n            // Inverse distortion model\n            // proposed by Heikkila\n            mx2_d = mx_d*mx_d;\n            my2_d = my_d*my_d;\n            mxy_d = mx_d*my_d;\n            rho2_d = mx2_d+my2_d;\n            rho4_d = rho2_d*rho2_d;\n            radDist_d = k1*rho2_d+k2*rho4_d;\n            Dx_d = mx_d*radDist_d + p2*(rho2_d+2*mx2_d) + 2*p1*mxy_d;\n            Dy_d = my_d*radDist_d + p1*(rho2_d+2*my2_d) + 2*p2*mxy_d;\n            inv_denom_d = 1/(1+4*k1*rho2_d+6*k2*rho4_d+8*p1*my_d+8*p2*mx_d);\n\n            mx_u = mx_d - inv_denom_d*Dx_d;\n            my_u = my_d - inv_denom_d*Dy_d;\n        }\n        else\n        {\n            // Recursive distortion model\n            int n = 6;\n            Eigen::Vector2d d_u;\n            distortion(Eigen::Vector2d(mx_d, my_d), d_u);\n            // Approximate value\n            mx_u = mx_d - d_u(0);\n            my_u = my_d - d_u(1);\n\n            for (int i = 1; i < n; ++i)\n            {\n                distortion(Eigen::Vector2d(mx_u, my_u), d_u);\n                mx_u = mx_d - d_u(0);\n                my_u = my_d - d_u(1);\n            }\n        }\n    }\n\n    // Lift normalised points to the sphere (inv_hslash)\n    double xi = mParameters.xi();\n    if (xi == 1.0)\n    {\n        lambda = 2.0 / (mx_u * mx_u + my_u * my_u + 1.0);\n        P << lambda * mx_u, lambda * my_u, lambda - 1.0;\n    }\n    else\n    {\n        lambda = (xi + sqrt(1.0 + (1.0 - xi * xi) * (mx_u * mx_u + my_u * my_u))) / (1.0 + mx_u * mx_u + my_u * my_u);\n        P << lambda * mx_u, lambda * my_u, lambda - xi;\n    }\n}\n\n/** \n * \\brief Lifts a point from the image plane to its projective ray\n *\n * \\param p image coordinates\n * \\param P coordinates of the projective ray\n */\nvoid\nCataCamera::liftProjective(const Eigen::Vector2d& p, Eigen::Vector3d& P) const\n{\n    double mx_d, my_d,mx2_d, mxy_d, my2_d, mx_u, my_u;\n    double rho2_d, rho4_d, radDist_d, Dx_d, Dy_d, inv_denom_d;\n    //double lambda;\n\n    // Lift points to normalised plane\n    mx_d = m_inv_K11 * p(0) + m_inv_K13;\n    my_d = m_inv_K22 * p(1) + m_inv_K23;\n\n    if (m_noDistortion)\n    {\n        mx_u = mx_d;\n        my_u = my_d;\n    }\n    else\n    {\n        if (0)\n        {\n            double k1 = mParameters.k1();\n            double k2 = mParameters.k2();\n            double p1 = mParameters.p1();\n            double p2 = mParameters.p2();\n\n            // Apply inverse distortion model\n            // proposed by Heikkila\n            mx2_d = mx_d*mx_d;\n            my2_d = my_d*my_d;\n            mxy_d = mx_d*my_d;\n            rho2_d = mx2_d+my2_d;\n            rho4_d = rho2_d*rho2_d;\n            radDist_d = k1*rho2_d+k2*rho4_d;\n            Dx_d = mx_d*radDist_d + p2*(rho2_d+2*mx2_d) + 2*p1*mxy_d;\n            Dy_d = my_d*radDist_d + p1*(rho2_d+2*my2_d) + 2*p2*mxy_d;\n            inv_denom_d = 1/(1+4*k1*rho2_d+6*k2*rho4_d+8*p1*my_d+8*p2*mx_d);\n\n            mx_u = mx_d - inv_denom_d*Dx_d;\n            my_u = my_d - inv_denom_d*Dy_d;\n        }\n        else\n        {\n            // Recursive distortion model\n            int n = 8;\n            Eigen::Vector2d d_u;\n            distortion(Eigen::Vector2d(mx_d, my_d), d_u);\n            // Approximate value\n            mx_u = mx_d - d_u(0);\n            my_u = my_d - d_u(1);\n\n            for (int i = 1; i < n; ++i)\n            {\n                distortion(Eigen::Vector2d(mx_u, my_u), d_u);\n                mx_u = mx_d - d_u(0);\n                my_u = my_d - d_u(1);\n            }\n        }\n    }\n\n    // Obtain a projective ray\n    double xi = mParameters.xi();\n    if (xi == 1.0)\n    {\n        P << mx_u, my_u, (1.0 - mx_u * mx_u - my_u * my_u) / 2.0;\n    }\n    else\n    {\n        // Reuse variable\n        rho2_d = mx_u * mx_u + my_u * my_u;\n        P << mx_u, my_u, 1.0 - xi * (rho2_d + 1.0) / (xi + sqrt(1.0 + (1.0 - xi * xi) * rho2_d));\n    }\n}\n\n\n/** \n * \\brief Project a 3D point (\\a x,\\a y,\\a z) to the image plane in (\\a u,\\a v)\n *\n * \\param P 3D point coordinates\n * \\param p return value, contains the image point coordinates\n */\nvoid\nCataCamera::spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p) const\n{\n    Eigen::Vector2d p_u, p_d;\n\n    // Project points to the normalised plane\n    double z = P(2) + mParameters.xi() * P.norm();\n    p_u << P(0) / z, P(1) / z;\n\n    if (m_noDistortion)\n    {\n        p_d = p_u;\n    }\n    else\n    {\n        // Apply distortion\n        Eigen::Vector2d d_u;\n        distortion(p_u, d_u);\n        p_d = p_u + d_u;\n    }\n\n    // Apply generalised projection matrix\n    p << mParameters.gamma1() * p_d(0) + mParameters.u0(),\n         mParameters.gamma2() * p_d(1) + mParameters.v0();\n}\n\n#if 0\n/** \n * \\brief Project a 3D point to the image plane and calculate Jacobian\n *\n * \\param P 3D point coordinates\n * \\param p return value, contains the image point coordinates\n */\nvoid\nCataCamera::spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p,\n                        Eigen::Matrix<double,2,3>& J) const\n{\n    double xi = mParameters.xi();\n\n    Eigen::Vector2d p_u, p_d;\n    double norm, inv_denom;\n    double dxdmx, dydmx, dxdmy, dydmy;\n\n    norm = P.norm();\n    // Project points to the normalised plane\n    inv_denom = 1.0 / (P(2) + xi * norm);\n    p_u << inv_denom * P(0), inv_denom * P(1);\n\n    // Calculate jacobian\n    inv_denom = inv_denom * inv_denom / norm;\n    double dudx = inv_denom * (norm * P(2) + xi * (P(1) * P(1) + P(2) * P(2)));\n    double dvdx = -inv_denom * xi * P(0) * P(1);\n    double dudy = dvdx;\n    double dvdy = inv_denom * (norm * P(2) + xi * (P(0) * P(0) + P(2) * P(2)));\n    inv_denom = inv_denom * (-xi * P(2) - norm); // reuse variable\n    double dudz = P(0) * inv_denom;\n    double dvdz = P(1) * inv_denom;\n\n    if (m_noDistortion)\n    {\n        p_d = p_u;\n    }\n    else\n    {\n        // Apply distortion\n        Eigen::Vector2d d_u;\n        distortion(p_u, d_u);\n        p_d = p_u + d_u;\n    }\n\n    double gamma1 = mParameters.gamma1();\n    double gamma2 = mParameters.gamma2();\n\n    // Make the product of the jacobians\n    // and add projection matrix jacobian\n    inv_denom = gamma1 * (dudx * dxdmx + dvdx * dxdmy); // reuse\n    dvdx = gamma2 * (dudx * dydmx + dvdx * dydmy);\n    dudx = inv_denom;\n\n    inv_denom = gamma1 * (dudy * dxdmx + dvdy * dxdmy); // reuse\n    dvdy = gamma2 * (dudy * dydmx + dvdy * dydmy);\n    dudy = inv_denom;\n\n    inv_denom = gamma1 * (dudz * dxdmx + dvdz * dxdmy); // reuse\n    dvdz = gamma2 * (dudz * dydmx + dvdz * dydmy);\n    dudz = inv_denom;\n    \n    // Apply generalised projection matrix\n    p << gamma1 * p_d(0) + mParameters.u0(),\n         gamma2 * p_d(1) + mParameters.v0();\n\n    J << dudx, dudy, dudz,\n         dvdx, dvdy, dvdz;\n}\n#endif\n\n/** \n * \\brief Projects an undistorted 2D point p_u to the image plane\n *\n * \\param p_u 2D point coordinates\n * \\return image point coordinates\n */\nvoid\nCataCamera::undistToPlane(const Eigen::Vector2d& p_u, Eigen::Vector2d& p) const\n{\n    Eigen::Vector2d p_d;\n\n    if (m_noDistortion)\n    {\n        p_d = p_u;\n    }\n    else\n    {\n        // Apply distortion\n        Eigen::Vector2d d_u;\n        distortion(p_u, d_u);\n        p_d = p_u + d_u;\n    }\n\n    // Apply generalised projection matrix\n    p << mParameters.gamma1() * p_d(0) + mParameters.u0(),\n         mParameters.gamma2() * p_d(1) + mParameters.v0();\n}\n\n/** \n * \\brief Apply distortion to input point (from the normalised plane)\n *  \n * \\param p_u undistorted coordinates of point on the normalised plane\n * \\return to obtain the distorted point: p_d = p_u + d_u\n */\nvoid\nCataCamera::distortion(const Eigen::Vector2d& p_u, Eigen::Vector2d& d_u) const\n{\n    double k1 = mParameters.k1();\n    double k2 = mParameters.k2();\n    double p1 = mParameters.p1();\n    double p2 = mParameters.p2();\n\n    double mx2_u, my2_u, mxy_u, rho2_u, rad_dist_u;\n\n    mx2_u = p_u(0) * p_u(0);\n    my2_u = p_u(1) * p_u(1);\n    mxy_u = p_u(0) * p_u(1);\n    rho2_u = mx2_u + my2_u;\n    rad_dist_u = k1 * rho2_u + k2 * rho2_u * rho2_u;\n    d_u << p_u(0) * rad_dist_u + 2.0 * p1 * mxy_u + p2 * (rho2_u + 2.0 * mx2_u),\n           p_u(1) * rad_dist_u + 2.0 * p2 * mxy_u + p1 * (rho2_u + 2.0 * my2_u);\n}\n\n/** \n * \\brief Apply distortion to input point (from the normalised plane)\n *        and calculate Jacobian\n *\n * \\param p_u undistorted coordinates of point on the normalised plane\n * \\return to obtain the distorted point: p_d = p_u + d_u\n */\nvoid\nCataCamera::distortion(const Eigen::Vector2d& p_u, Eigen::Vector2d& d_u,\n                       Eigen::Matrix2d& J) const\n{\n    double k1 = mParameters.k1();\n    double k2 = mParameters.k2();\n    double p1 = mParameters.p1();\n    double p2 = mParameters.p2();\n\n    double mx2_u, my2_u, mxy_u, rho2_u, rad_dist_u;\n\n    mx2_u = p_u(0) * p_u(0);\n    my2_u = p_u(1) * p_u(1);\n    mxy_u = p_u(0) * p_u(1);\n    rho2_u = mx2_u + my2_u;\n    rad_dist_u = k1 * rho2_u + k2 * rho2_u * rho2_u;\n    d_u << p_u(0) * rad_dist_u + 2.0 * p1 * mxy_u + p2 * (rho2_u + 2.0 * mx2_u),\n           p_u(1) * rad_dist_u + 2.0 * p2 * mxy_u + p1 * (rho2_u + 2.0 * my2_u);\n\n    double dxdmx = 1.0 + rad_dist_u + k1 * 2.0 * mx2_u + k2 * rho2_u * 4.0 * mx2_u + 2.0 * p1 * p_u(1) + 6.0 * p2 * p_u(0);\n    double dydmx = k1 * 2.0 * p_u(0) * p_u(1) + k2 * 4.0 * rho2_u * p_u(0) * p_u(1) + p1 * 2.0 * p_u(0) + 2.0 * p2 * p_u(1);\n    double dxdmy = dydmx;\n    double dydmy = 1.0 + rad_dist_u + k1 * 2.0 * my2_u + k2 * rho2_u * 4.0 * my2_u + 6.0 * p1 * p_u(1) + 2.0 * p2 * p_u(0);\n\n    J << dxdmx, dxdmy,\n         dydmx, dydmy;\n}\n\nvoid\nCataCamera::initUndistortMap(cv::Mat& map1, cv::Mat& map2, double fScale) const\n{\n    cv::Size imageSize(mParameters.imageWidth(), mParameters.imageHeight());\n\n    cv::Mat mapX = cv::Mat::zeros(imageSize, CV_32F);\n    cv::Mat mapY = cv::Mat::zeros(imageSize, CV_32F);\n\n    for (int v = 0; v < imageSize.height; ++v)\n    {\n        for (int u = 0; u < imageSize.width; ++u)\n        {\n            double mx_u = m_inv_K11 / fScale * u + m_inv_K13 / fScale;\n            double my_u = m_inv_K22 / fScale * v + m_inv_K23 / fScale;\n\n            double xi = mParameters.xi();\n            double d2 = mx_u * mx_u + my_u * my_u;\n\n            Eigen::Vector3d P;\n            P << mx_u, my_u, 1.0 - xi * (d2 + 1.0) / (xi + sqrt(1.0 + (1.0 - xi * xi) * d2));\n\n            Eigen::Vector2d p;\n            spaceToPlane(P, p);\n\n            mapX.at<float>(v,u) = p(0);\n            mapY.at<float>(v,u) = p(1);\n        }\n    }\n\n    cv::convertMaps(mapX, mapY, map1, map2, CV_32FC1, false);\n}\n\ncv::Mat\nCataCamera::initUndistortRectifyMap(cv::Mat& map1, cv::Mat& map2,\n                                    float fx, float fy,\n                                    cv::Size imageSize,\n                                    float cx, float cy,\n                                    cv::Mat rmat) const\n{\n    if (imageSize == cv::Size(0, 0))\n    {\n        imageSize = cv::Size(mParameters.imageWidth(), mParameters.imageHeight());\n    }\n\n    cv::Mat mapX = cv::Mat::zeros(imageSize.height, imageSize.width, CV_32F);\n    cv::Mat mapY = cv::Mat::zeros(imageSize.height, imageSize.width, CV_32F);\n\n    Eigen::Matrix3f K_rect;\n\n    if (cx == -1.0f && cy == -1.0f)\n    {\n        K_rect << fx, 0, imageSize.width / 2,\n                  0, fy, imageSize.height / 2,\n                  0, 0, 1;\n    }\n    else\n    {\n        K_rect << fx, 0, cx,\n                  0, fy, cy,\n                  0, 0, 1;\n    }\n\n    if (fx == -1.0f || fy == -1.0f)\n    {\n        K_rect(0,0) = mParameters.gamma1();\n        K_rect(1,1) = mParameters.gamma2();\n    }\n\n    Eigen::Matrix3f K_rect_inv = K_rect.inverse();\n\n    Eigen::Matrix3f R, R_inv;\n    cv::cv2eigen(rmat, R);\n    R_inv = R.inverse();\n\n    for (int v = 0; v < imageSize.height; ++v)\n    {\n        for (int u = 0; u < imageSize.width; ++u)\n        {\n            Eigen::Vector3f xo;\n            xo << u, v, 1;\n\n            Eigen::Vector3f uo = R_inv * K_rect_inv * xo;\n\n            Eigen::Vector2d p;\n            spaceToPlane(uo.cast<double>(), p);\n\n            mapX.at<float>(v,u) = p(0);\n            mapY.at<float>(v,u) = p(1);\n        }\n    }\n\n    cv::convertMaps(mapX, mapY, map1, map2, CV_32FC1, false);\n\n    cv::Mat K_rect_cv;\n    cv::eigen2cv(K_rect, K_rect_cv);\n    return K_rect_cv;\n}\n\nint\nCataCamera::parameterCount(void) const\n{\n    return 9;\n}\n\nconst CataCamera::Parameters&\nCataCamera::getParameters(void) const\n{\n    return mParameters;\n}\n\nvoid\nCataCamera::setParameters(const CataCamera::Parameters& parameters)\n{\n    mParameters = parameters;\n\n    if ((mParameters.k1() == 0.0) &&\n        (mParameters.k2() == 0.0) &&\n        (mParameters.p1() == 0.0) &&\n        (mParameters.p2() == 0.0))\n    {\n        m_noDistortion = true;\n    }\n    else\n    {\n        m_noDistortion = false;\n    }\n\n    m_inv_K11 = 1.0 / mParameters.gamma1();\n    m_inv_K13 = -mParameters.u0() / mParameters.gamma1();\n    m_inv_K22 = 1.0 / mParameters.gamma2();\n    m_inv_K23 = -mParameters.v0() / mParameters.gamma2();\n}\n\nvoid\nCataCamera::readParameters(const std::vector<double>& parameterVec)\n{\n    if ((int)parameterVec.size() != parameterCount())\n    {\n        return;\n    }\n\n    Parameters params = getParameters();\n\n    params.xi() = parameterVec.at(0);\n    params.k1() = parameterVec.at(1);\n    params.k2() = parameterVec.at(2);\n    params.p1() = parameterVec.at(3);\n    params.p2() = parameterVec.at(4);\n    params.gamma1() = parameterVec.at(5);\n    params.gamma2() = parameterVec.at(6);\n    params.u0() = parameterVec.at(7);\n    params.v0() = parameterVec.at(8);\n\n    setParameters(params);\n}\n\nvoid\nCataCamera::writeParameters(std::vector<double>& parameterVec) const\n{\n    parameterVec.resize(parameterCount());\n    parameterVec.at(0) = mParameters.xi();\n    parameterVec.at(1) = mParameters.k1();\n    parameterVec.at(2) = mParameters.k2();\n    parameterVec.at(3) = mParameters.p1();\n    parameterVec.at(4) = mParameters.p2();\n    parameterVec.at(5) = mParameters.gamma1();\n    parameterVec.at(6) = mParameters.gamma2();\n    parameterVec.at(7) = mParameters.u0();\n    parameterVec.at(8) = mParameters.v0();\n}\n\nvoid\nCataCamera::writeParametersToYamlFile(const std::string& filename) const\n{\n    mParameters.writeToYamlFile(filename);\n}\n\nstd::string\nCataCamera::parametersToString(void) const\n{\n    std::ostringstream oss;\n    oss << mParameters;\n\n    return oss.str();\n}\n\n}\n"
  },
  {
    "path": "src/utils/camodocal/src/camera_models/CostFunctionFactory.cc",
    "content": "#include \"camodocal/camera_models/CostFunctionFactory.h\"\n\n#include \"ceres/ceres.h\"\n#include \"camodocal/camera_models/CataCamera.h\"\n#include \"camodocal/camera_models/EquidistantCamera.h\"\n#include \"camodocal/camera_models/PinholeCamera.h\"\n#include \"camodocal/camera_models/ScaramuzzaCamera.h\"\n\nnamespace camodocal\n{\n\ntemplate<typename T>\nvoid\nworldToCameraTransform(const T* const q_cam_odo, const T* const t_cam_odo,\n                       const T* const p_odo, const T* const att_odo,\n                       T* q, T* t, bool optimize_cam_odo_z = true)\n{\n    Eigen::Quaternion<T> q_z_inv(cos(att_odo[0] / T(2)), T(0), T(0), -sin(att_odo[0] / T(2)));\n    Eigen::Quaternion<T> q_y_inv(cos(att_odo[1] / T(2)), T(0), -sin(att_odo[1] / T(2)), T(0));\n    Eigen::Quaternion<T> q_x_inv(cos(att_odo[2] / T(2)), -sin(att_odo[2] / T(2)), T(0), T(0));\n\n    Eigen::Quaternion<T> q_zyx_inv = q_x_inv * q_y_inv * q_z_inv;\n\n    T q_odo[4] = {q_zyx_inv.w(), q_zyx_inv.x(), q_zyx_inv.y(), q_zyx_inv.z()};\n\n    T q_odo_cam[4] = {q_cam_odo[3], -q_cam_odo[0], -q_cam_odo[1], -q_cam_odo[2]};\n\n    T q0[4];\n    ceres::QuaternionProduct(q_odo_cam, q_odo, q0);\n\n    T t0[3];\n    T t_odo[3] = {p_odo[0], p_odo[1], p_odo[2]};\n\n    ceres::QuaternionRotatePoint(q_odo, t_odo, t0);\n\n    t0[0] += t_cam_odo[0];\n    t0[1] += t_cam_odo[1];\n\n    if (optimize_cam_odo_z)\n    {\n        t0[2] += t_cam_odo[2];\n    }\n\n    ceres::QuaternionRotatePoint(q_odo_cam, t0, t);\n    t[0] = -t[0];\n    t[1] = -t[1];\n    t[2] = -t[2];\n\n    // Convert quaternion from Ceres convention (w, x, y, z)\n    // to Eigen convention (x, y, z, w)\n    q[0] = q0[1];\n    q[1] = q0[2];\n    q[2] = q0[3];\n    q[3] = q0[0];\n}\n\ntemplate<class CameraT>\nclass ReprojectionError1\n{\npublic:\n    EIGEN_MAKE_ALIGNED_OPERATOR_NEW\n\n    ReprojectionError1(const Eigen::Vector3d& observed_P,\n                       const Eigen::Vector2d& observed_p)\n        : m_observed_P(observed_P), m_observed_p(observed_p)\n        , m_sqrtPrecisionMat(Eigen::Matrix2d::Identity()) {}\n\n    ReprojectionError1(const Eigen::Vector3d& observed_P,\n                       const Eigen::Vector2d& observed_p,\n                       const Eigen::Matrix2d& sqrtPrecisionMat)\n        : m_observed_P(observed_P), m_observed_p(observed_p)\n        , m_sqrtPrecisionMat(sqrtPrecisionMat) {}\n\n    ReprojectionError1(const std::vector<double>& intrinsic_params,\n                       const Eigen::Vector3d& observed_P,\n                       const Eigen::Vector2d& observed_p)\n        : m_intrinsic_params(intrinsic_params)\n        , m_observed_P(observed_P), m_observed_p(observed_p) {}\n\n    // variables: camera intrinsics and camera extrinsics\n    template <typename T>\n    bool operator()(const T* const intrinsic_params,\n                    const T* const q,\n                    const T* const t,\n                    T* residuals) const\n    {\n        Eigen::Matrix<T, 3, 1> P = m_observed_P.cast<T>();\n\n        Eigen::Matrix<T, 2, 1> predicted_p;\n        CameraT::spaceToPlane(intrinsic_params, q, t, P, predicted_p);\n\n        Eigen::Matrix<T, 2, 1> e = predicted_p - m_observed_p.cast<T>();\n\n        Eigen::Matrix<T, 2, 1> e_weighted = m_sqrtPrecisionMat.cast<T>() * e;\n\n        residuals[0] = e_weighted(0);\n        residuals[1] = e_weighted(1);\n\n        return true;\n    }\n\n    // variables: camera-odometry transforms and odometry poses\n    template <typename T>\n    bool operator()(const T* const q_cam_odo, const T* const t_cam_odo,\n                    const T* const p_odo, const T* const att_odo,\n                    T* residuals) const\n    {\n        T q[4], t[3];\n        worldToCameraTransform(q_cam_odo, t_cam_odo, p_odo, att_odo, q, t);\n\n        Eigen::Matrix<T, 3, 1> P = m_observed_P.cast<T>();\n\n        std::vector<T> intrinsic_params(m_intrinsic_params.begin(), m_intrinsic_params.end());\n\n        // project 3D object point to the image plane\n        Eigen::Matrix<T, 2, 1> predicted_p;\n        CameraT::spaceToPlane(intrinsic_params.data(), q, t, P, predicted_p);\n\n        residuals[0] = predicted_p(0) - T(m_observed_p(0));\n        residuals[1] = predicted_p(1) - T(m_observed_p(1));\n\n        return true;\n    }\n\n//private:\n    // camera intrinsics\n    std::vector<double> m_intrinsic_params;\n\n    // observed 3D point\n    Eigen::Vector3d m_observed_P;\n\n    // observed 2D point\n    Eigen::Vector2d m_observed_p;\n\n    // square root of precision matrix\n    Eigen::Matrix2d m_sqrtPrecisionMat;\n};\n\n// variables: camera extrinsics, 3D point\ntemplate<class CameraT>\nclass ReprojectionError2\n{\npublic:\n    EIGEN_MAKE_ALIGNED_OPERATOR_NEW\n\n    ReprojectionError2(const std::vector<double>& intrinsic_params,\n                       const Eigen::Vector2d& observed_p)\n        : m_intrinsic_params(intrinsic_params), m_observed_p(observed_p) {}\n\n    template <typename T>\n    bool operator()(const T* const q, const T* const t,\n                    const T* const point, T* residuals) const\n    {\n        Eigen::Matrix<T, 3, 1> P;\n        P(0) = T(point[0]);\n        P(1) = T(point[1]);\n        P(2) = T(point[2]);\n\n        std::vector<T> intrinsic_params(m_intrinsic_params.begin(), m_intrinsic_params.end());\n\n        // project 3D object point to the image plane\n        Eigen::Matrix<T, 2, 1> predicted_p;\n        CameraT::spaceToPlane(intrinsic_params.data(), q, t, P, predicted_p);\n\n        residuals[0] = predicted_p(0) - T(m_observed_p(0));\n        residuals[1] = predicted_p(1) - T(m_observed_p(1));\n\n        return true;\n    }\n\nprivate:\n    // camera intrinsics\n    std::vector<double> m_intrinsic_params;\n\n    // observed 2D point\n    Eigen::Vector2d m_observed_p;\n};\n\ntemplate<class CameraT>\nclass ReprojectionError3\n{\npublic:\n    EIGEN_MAKE_ALIGNED_OPERATOR_NEW\n\n    ReprojectionError3(const Eigen::Vector2d& observed_p)\n        : m_observed_p(observed_p)\n        , m_sqrtPrecisionMat(Eigen::Matrix2d::Identity())\n        , m_optimize_cam_odo_z(true) {}\n\n    ReprojectionError3(const Eigen::Vector2d& observed_p,\n                       const Eigen::Matrix2d& sqrtPrecisionMat)\n        : m_observed_p(observed_p)\n        , m_sqrtPrecisionMat(sqrtPrecisionMat)\n        , m_optimize_cam_odo_z(true) {}\n\n    ReprojectionError3(const std::vector<double>& intrinsic_params,\n                       const Eigen::Vector2d& observed_p)\n        : m_intrinsic_params(intrinsic_params)\n        , m_observed_p(observed_p)\n        , m_sqrtPrecisionMat(Eigen::Matrix2d::Identity())\n        , m_optimize_cam_odo_z(true) {}\n\n    ReprojectionError3(const std::vector<double>& intrinsic_params,\n                       const Eigen::Vector2d& observed_p,\n                       const Eigen::Matrix2d& sqrtPrecisionMat)\n        : m_intrinsic_params(intrinsic_params)\n        , m_observed_p(observed_p)\n        , m_sqrtPrecisionMat(sqrtPrecisionMat)\n        , m_optimize_cam_odo_z(true) {}\n\n\n    ReprojectionError3(const std::vector<double>& intrinsic_params,\n                       const Eigen::Vector3d& odo_pos,\n                       const Eigen::Vector3d& odo_att,\n                       const Eigen::Vector2d& observed_p,\n                       bool optimize_cam_odo_z)\n        : m_intrinsic_params(intrinsic_params)\n        , m_odo_pos(odo_pos), m_odo_att(odo_att)\n        , m_observed_p(observed_p)\n        , m_optimize_cam_odo_z(optimize_cam_odo_z) {}\n\n    ReprojectionError3(const std::vector<double>& intrinsic_params,\n                       const Eigen::Quaterniond& cam_odo_q,\n                       const Eigen::Vector3d& cam_odo_t,\n                       const Eigen::Vector3d& odo_pos,\n                       const Eigen::Vector3d& odo_att,\n                       const Eigen::Vector2d& observed_p)\n        : m_intrinsic_params(intrinsic_params)\n        , m_cam_odo_q(cam_odo_q), m_cam_odo_t(cam_odo_t)\n        , m_odo_pos(odo_pos), m_odo_att(odo_att)\n        , m_observed_p(observed_p)\n        , m_optimize_cam_odo_z(true) {}\n\n    // variables: camera intrinsics, camera-to-odometry transform,\n    //            odometry extrinsics, 3D point\n    template <typename T>\n    bool operator()(const T* const intrinsic_params,\n                    const T* const q_cam_odo, const T* const t_cam_odo,\n                    const T* const p_odo, const T* const att_odo,\n                    const T* const point, T* residuals) const\n    {\n        T q[4], t[3];\n        worldToCameraTransform(q_cam_odo, t_cam_odo, p_odo, att_odo, q, t, m_optimize_cam_odo_z);\n\n        Eigen::Matrix<T, 3, 1> P(point[0], point[1], point[2]);\n\n        // project 3D object point to the image plane\n        Eigen::Matrix<T, 2, 1> predicted_p;\n        CameraT::spaceToPlane(intrinsic_params, q, t, P, predicted_p);\n\n        Eigen::Matrix<T, 2, 1> err = predicted_p - m_observed_p.cast<T>();\n        Eigen::Matrix<T, 2, 1> err_weighted = m_sqrtPrecisionMat.cast<T>() * err;\n\n        residuals[0] = err_weighted(0);\n        residuals[1] = err_weighted(1);\n\n        return true;\n    }\n\n    // variables: camera-to-odometry transform, 3D point\n    template <typename T>\n    bool operator()(const T* const q_cam_odo, const T* const t_cam_odo,\n                    const T* const point, T* residuals) const\n    {\n        T p_odo[3] = {T(m_odo_pos(0)), T(m_odo_pos(1)), T(m_odo_pos(2))};\n        T att_odo[3] = {T(m_odo_att(0)), T(m_odo_att(1)), T(m_odo_att(2))};\n        T q[4], t[3];\n\n        worldToCameraTransform(q_cam_odo, t_cam_odo, p_odo, att_odo, q, t, m_optimize_cam_odo_z);\n\n        std::vector<T> intrinsic_params(m_intrinsic_params.begin(), m_intrinsic_params.end());\n        Eigen::Matrix<T, 3, 1> P(point[0], point[1], point[2]);\n\n        // project 3D object point to the image plane\n        Eigen::Matrix<T, 2, 1> predicted_p;\n        CameraT::spaceToPlane(intrinsic_params.data(), q, t, P, predicted_p);\n\n        residuals[0] = predicted_p(0) - T(m_observed_p(0));\n        residuals[1] = predicted_p(1) - T(m_observed_p(1));\n\n        return true;\n    }\n\n    // variables: camera-to-odometry transform, odometry extrinsics, 3D point\n    template <typename T>\n    bool operator()(const T* const q_cam_odo, const T* const t_cam_odo,\n                    const T* const p_odo, const T* const att_odo,\n                    const T* const point, T* residuals) const\n    {\n        T q[4], t[3];\n        worldToCameraTransform(q_cam_odo, t_cam_odo, p_odo, att_odo, q, t, m_optimize_cam_odo_z);\n\n        std::vector<T> intrinsic_params(m_intrinsic_params.begin(), m_intrinsic_params.end());\n        Eigen::Matrix<T, 3, 1> P(point[0], point[1], point[2]);\n\n        // project 3D object point to the image plane\n        Eigen::Matrix<T, 2, 1> predicted_p;\n        CameraT::spaceToPlane(intrinsic_params.data(), q, t, P, predicted_p);\n\n        Eigen::Matrix<T, 2, 1> err = predicted_p - m_observed_p.cast<T>();\n        Eigen::Matrix<T, 2, 1> err_weighted = m_sqrtPrecisionMat.cast<T>() * err;\n\n        residuals[0] = err_weighted(0);\n        residuals[1] = err_weighted(1);\n\n        return true;\n    }\n\n    // variables: 3D point\n    template <typename T>\n    bool operator()(const T* const point, T* residuals) const\n    {\n        T q_cam_odo[4] = {T(m_cam_odo_q.coeffs()(0)), T(m_cam_odo_q.coeffs()(1)), T(m_cam_odo_q.coeffs()(2)), T(m_cam_odo_q.coeffs()(3))};\n        T t_cam_odo[3] = {T(m_cam_odo_t(0)), T(m_cam_odo_t(1)), T(m_cam_odo_t(2))};\n        T p_odo[3] = {T(m_odo_pos(0)), T(m_odo_pos(1)), T(m_odo_pos(2))};\n        T att_odo[3] = {T(m_odo_att(0)), T(m_odo_att(1)), T(m_odo_att(2))};\n        T q[4], t[3];\n\n        worldToCameraTransform(q_cam_odo, t_cam_odo, p_odo, att_odo, q, t, m_optimize_cam_odo_z);\n\n        std::vector<T> intrinsic_params(m_intrinsic_params.begin(), m_intrinsic_params.end());\n        Eigen::Matrix<T, 3, 1> P(point[0], point[1], point[2]);\n\n        // project 3D object point to the image plane\n        Eigen::Matrix<T, 2, 1> predicted_p;\n        CameraT::spaceToPlane(intrinsic_params.data(), q, t, P, predicted_p);\n\n        residuals[0] = predicted_p(0) - T(m_observed_p(0));\n        residuals[1] = predicted_p(1) - T(m_observed_p(1));\n\n        return true;\n    }\n\nprivate:\n    // camera intrinsics\n    std::vector<double> m_intrinsic_params;\n\n    // observed camera-odometry transform\n    Eigen::Quaterniond m_cam_odo_q;\n    Eigen::Vector3d m_cam_odo_t;\n\n    // observed odometry\n    Eigen::Vector3d m_odo_pos;\n    Eigen::Vector3d m_odo_att;\n\n    // observed 2D point\n    Eigen::Vector2d m_observed_p;\n\n    Eigen::Matrix2d m_sqrtPrecisionMat;\n\n    bool m_optimize_cam_odo_z;\n};\n\n// variables: camera intrinsics and camera extrinsics\ntemplate<class CameraT>\nclass StereoReprojectionError\n{\npublic:\n    EIGEN_MAKE_ALIGNED_OPERATOR_NEW\n\n    StereoReprojectionError(const Eigen::Vector3d& observed_P,\n                            const Eigen::Vector2d& observed_p_l,\n                            const Eigen::Vector2d& observed_p_r)\n        : m_observed_P(observed_P)\n        , m_observed_p_l(observed_p_l)\n        , m_observed_p_r(observed_p_r)\n    {\n\n    }\n\n    template <typename T>\n    bool operator()(const T* const intrinsic_params_l,\n                    const T* const intrinsic_params_r,\n                    const T* const q_l,\n                    const T* const t_l,\n                    const T* const q_l_r,\n                    const T* const t_l_r,\n                    T* residuals) const\n    {\n        Eigen::Matrix<T, 3, 1> P;\n        P(0) = T(m_observed_P(0));\n        P(1) = T(m_observed_P(1));\n        P(2) = T(m_observed_P(2));\n\n        Eigen::Matrix<T, 2, 1> predicted_p_l;\n        CameraT::spaceToPlane(intrinsic_params_l, q_l, t_l, P, predicted_p_l);\n\n        Eigen::Quaternion<T> q_r = Eigen::Quaternion<T>(q_l_r) * Eigen::Quaternion<T>(q_l);\n\n        Eigen::Matrix<T, 3, 1> t_r;\n        t_r(0) = t_l[0];\n        t_r(1) = t_l[1];\n        t_r(2) = t_l[2];\n\n        t_r = Eigen::Quaternion<T>(q_l_r) * t_r;\n        t_r(0) += t_l_r[0];\n        t_r(1) += t_l_r[1];\n        t_r(2) += t_l_r[2];\n\n        Eigen::Matrix<T, 2, 1> predicted_p_r;\n        CameraT::spaceToPlane(intrinsic_params_r, q_r.coeffs().data(), t_r.data(), P, predicted_p_r);\n\n        residuals[0] = predicted_p_l(0) - T(m_observed_p_l(0));\n        residuals[1] = predicted_p_l(1) - T(m_observed_p_l(1));\n        residuals[2] = predicted_p_r(0) - T(m_observed_p_r(0));\n        residuals[3] = predicted_p_r(1) - T(m_observed_p_r(1));\n\n        return true;\n    }\n\nprivate:\n    // observed 3D point\n    Eigen::Vector3d m_observed_P;\n\n    // observed 2D point\n    Eigen::Vector2d m_observed_p_l;\n    Eigen::Vector2d m_observed_p_r;\n};\n\ntemplate <class CameraT>\nclass ComprehensionError {\n  public:\n    EIGEN_MAKE_ALIGNED_OPERATOR_NEW\n\n    ComprehensionError(const Eigen::Vector3d& observed_P, const Eigen::Vector2d& observed_p)\n        : m_observed_P(observed_P),\n          m_observed_p(observed_p),\n          m_sqrtPrecisionMat(Eigen::Matrix2d::Identity()) {\n    }\n\n    template <typename T>\n    bool operator()(const T* const intrinsic_params, const T* const q, const T* const t,\n                    T* residuals) const {\n        {\n            Eigen::Matrix<T, 2, 1> p = m_observed_p.cast<T>();\n        \n            Eigen::Matrix<T, 3, 1> predicted_img_P;\n            CameraT::LiftToSphere(intrinsic_params, p, predicted_img_P);\n                \n            Eigen::Matrix<T, 2, 1> predicted_p;\n            CameraT::SphereToPlane(intrinsic_params, predicted_img_P, predicted_p);\n\n            Eigen::Matrix<T, 2, 1> e = predicted_p - m_observed_p.cast<T>();\n\n            Eigen::Matrix<T, 2, 1> e_weighted = m_sqrtPrecisionMat.cast<T>() * e;\n\n            residuals[0] = e_weighted(0);\n            residuals[1] = e_weighted(1);\n        }\n\n        {\n            Eigen::Matrix<T, 3, 1> P = m_observed_P.cast<T>();\n\n            Eigen::Matrix<T, 2, 1> predicted_p;\n            CameraT::spaceToPlane(intrinsic_params, q, t, P, predicted_p);\n\n            Eigen::Matrix<T, 2, 1> e = predicted_p - m_observed_p.cast<T>();\n\n            Eigen::Matrix<T, 2, 1> e_weighted = m_sqrtPrecisionMat.cast<T>() * e;\n\n            residuals[2] = e_weighted(0);\n            residuals[3] = e_weighted(1);\n        }\n\n        return true;\n    }\n\n    // private:\n    // camera intrinsics\n    // std::vector<double> m_intrinsic_params;\n\n    // observed 3D point\n    Eigen::Vector3d m_observed_P;\n\n    // observed 2D point\n    Eigen::Vector2d m_observed_p;\n\n    // square root of precision matrix\n    Eigen::Matrix2d m_sqrtPrecisionMat;\n};\n\nboost::shared_ptr<CostFunctionFactory> CostFunctionFactory::m_instance;\n\nCostFunctionFactory::CostFunctionFactory()\n{\n\n}\n\nboost::shared_ptr<CostFunctionFactory>\nCostFunctionFactory::instance(void)\n{\n    if (m_instance.get() == 0)\n    {\n        m_instance.reset(new CostFunctionFactory);\n    }\n\n    return m_instance;\n}\n\nceres::CostFunction*\nCostFunctionFactory::generateCostFunction(const CameraConstPtr& camera,\n        const Eigen::Vector3d& observed_P,\n        const Eigen::Vector2d& observed_p,\n        int flags) const\n{\n    ceres::CostFunction* costFunction = 0;\n\n    std::vector<double> intrinsic_params;\n    camera->writeParameters(intrinsic_params);\n\n    switch (flags)\n    {\n    case CAMERA_INTRINSICS | CAMERA_POSE:\n        switch (camera->modelType())\n        {\n        case Camera::KANNALA_BRANDT:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError1<EquidistantCamera>, 2, 8, 4, 3>(\n                new ReprojectionError1<EquidistantCamera>(observed_P, observed_p));\n            break;\n        case Camera::PINHOLE:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError1<PinholeCamera>, 2, 8, 4, 3>(\n                new ReprojectionError1<PinholeCamera>(observed_P, observed_p));\n            break;\n        case Camera::MEI:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError1<CataCamera>, 2, 9, 4, 3>(\n                new ReprojectionError1<CataCamera>(observed_P, observed_p));\n            break;\n        case Camera::SCARAMUZZA:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ComprehensionError<OCAMCamera>, 4, SCARAMUZZA_CAMERA_NUM_PARAMS, 4, 3>(\n                new ComprehensionError<OCAMCamera>(observed_P, observed_p));\n                // new ceres::AutoDiffCostFunction<ReprojectionError1<OCAMCamera>, 2, SCARAMUZZA_CAMERA_NUM_PARAMS, 4, 3>(\n                // new ReprojectionError1<OCAMCamera>(observed_P, observed_p));\n            break;\n        }\n        break;\n    case CAMERA_ODOMETRY_TRANSFORM | ODOMETRY_6D_POSE:\n        switch (camera->modelType())\n        {\n        case Camera::KANNALA_BRANDT:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError1<EquidistantCamera>, 2, 4, 3, 3, 3>(\n                new ReprojectionError1<EquidistantCamera>(intrinsic_params, observed_P, observed_p));\n            break;\n        case Camera::PINHOLE:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError1<PinholeCamera>, 2, 4, 3, 3, 3>(\n                new ReprojectionError1<PinholeCamera>(intrinsic_params, observed_P, observed_p));\n            break;\n        case Camera::MEI:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError1<CataCamera>, 2, 4, 3, 3, 3>(\n                new ReprojectionError1<CataCamera>(intrinsic_params, observed_P, observed_p));\n            break;\n        case Camera::SCARAMUZZA:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError1<OCAMCamera>, 2, 4, 3, 3, 3>(\n                new ReprojectionError1<OCAMCamera>(intrinsic_params, observed_P, observed_p));\n            break;\n        }\n        break;\n    }\n\n    return costFunction;\n}\n\nceres::CostFunction*\nCostFunctionFactory::generateCostFunction(const CameraConstPtr& camera,\n        const Eigen::Vector3d& observed_P,\n        const Eigen::Vector2d& observed_p,\n        const Eigen::Matrix2d& sqrtPrecisionMat,\n        int flags) const\n{\n    ceres::CostFunction* costFunction = 0;\n\n    std::vector<double> intrinsic_params;\n    camera->writeParameters(intrinsic_params);\n\n    switch (flags)\n    {\n    case CAMERA_INTRINSICS | CAMERA_POSE:\n        switch (camera->modelType())\n        {\n        case Camera::KANNALA_BRANDT:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError1<EquidistantCamera>, 2, 8, 4, 3>(\n                new ReprojectionError1<EquidistantCamera>(observed_P, observed_p, sqrtPrecisionMat));\n            break;\n        case Camera::PINHOLE:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError1<PinholeCamera>, 2, 8, 4, 3>(\n                new ReprojectionError1<PinholeCamera>(observed_P, observed_p, sqrtPrecisionMat));\n            break;\n        case Camera::MEI:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError1<CataCamera>, 2, 9, 4, 3>(\n                new ReprojectionError1<CataCamera>(observed_P, observed_p, sqrtPrecisionMat));\n            break;\n        case Camera::SCARAMUZZA:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError1<OCAMCamera>, 2, SCARAMUZZA_CAMERA_NUM_PARAMS, 4, 3>(\n                new ReprojectionError1<OCAMCamera>(observed_P, observed_p, sqrtPrecisionMat));\n            break;\n        }\n        break;\n    }\n\n    return costFunction;\n}\n\nceres::CostFunction*\nCostFunctionFactory::generateCostFunction(const CameraConstPtr& camera,\n        const Eigen::Vector2d& observed_p,\n        int flags, bool optimize_cam_odo_z) const\n{\n    ceres::CostFunction* costFunction = 0;\n\n    std::vector<double> intrinsic_params;\n    camera->writeParameters(intrinsic_params);\n\n    switch (flags)\n    {\n    case CAMERA_POSE | POINT_3D:\n        switch (camera->modelType())\n        {\n        case Camera::KANNALA_BRANDT:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError2<EquidistantCamera>, 2, 4, 3, 3>(\n                new ReprojectionError2<EquidistantCamera>(intrinsic_params, observed_p));\n            break;\n        case Camera::PINHOLE:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError2<PinholeCamera>, 2, 4, 3, 3>(\n                new ReprojectionError2<PinholeCamera>(intrinsic_params, observed_p));\n            break;\n        case Camera::MEI:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError2<CataCamera>, 2, 4, 3, 3>(\n                new ReprojectionError2<CataCamera>(intrinsic_params, observed_p));\n            break;\n        case Camera::SCARAMUZZA:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError2<OCAMCamera>, 2, 4, 3, 3>(\n                new ReprojectionError2<OCAMCamera>(intrinsic_params, observed_p));\n            break;\n        }\n        break;\n    case CAMERA_ODOMETRY_TRANSFORM | ODOMETRY_3D_POSE | POINT_3D:\n        switch (camera->modelType())\n        {\n        case Camera::KANNALA_BRANDT:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<EquidistantCamera>, 2, 4, 3, 2, 1, 3>(\n                    new ReprojectionError3<EquidistantCamera>(intrinsic_params, observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 4, 2, 2, 1, 3>(\n                    new ReprojectionError3<PinholeCamera>(intrinsic_params, observed_p));\n            }\n            break;\n        case Camera::PINHOLE:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 4, 3, 2, 1, 3>(\n                    new ReprojectionError3<PinholeCamera>(intrinsic_params, observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 4, 2, 2, 1, 3>(\n                    new ReprojectionError3<PinholeCamera>(intrinsic_params, observed_p));\n            }\n            break;\n        case Camera::MEI:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 4, 3, 2, 1, 3>(\n                    new ReprojectionError3<CataCamera>(intrinsic_params, observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 4, 2, 2, 1, 3>(\n                    new ReprojectionError3<CataCamera>(intrinsic_params, observed_p));\n            }\n            break;\n        case Camera::SCARAMUZZA:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, 4, 3, 2, 1, 3>(\n                    new ReprojectionError3<OCAMCamera>(intrinsic_params, observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, 4, 2, 2, 1, 3>(\n                    new ReprojectionError3<OCAMCamera>(intrinsic_params, observed_p));\n            }\n            break;\n        }\n        break;\n    case CAMERA_ODOMETRY_TRANSFORM | ODOMETRY_6D_POSE | POINT_3D:\n        switch (camera->modelType())\n        {\n        case Camera::KANNALA_BRANDT:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<EquidistantCamera>, 2, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<EquidistantCamera>(intrinsic_params, observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<PinholeCamera>(intrinsic_params, observed_p));\n            }\n            break;\n        case Camera::PINHOLE:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<PinholeCamera>(intrinsic_params, observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<PinholeCamera>(intrinsic_params, observed_p));\n            }\n            break;\n        case Camera::MEI:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<CataCamera>(intrinsic_params, observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<CataCamera>(intrinsic_params, observed_p));\n            }\n            break;\n        case Camera::SCARAMUZZA:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<OCAMCamera>(intrinsic_params, observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<OCAMCamera>(intrinsic_params, observed_p));\n            }\n            break;\n        }\n        break;\n    case CAMERA_INTRINSICS | CAMERA_ODOMETRY_TRANSFORM | ODOMETRY_3D_POSE | POINT_3D:\n        switch (camera->modelType())\n        {\n        case Camera::KANNALA_BRANDT:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<EquidistantCamera>, 2, 8, 4, 3, 2, 1, 3>(\n                    new ReprojectionError3<EquidistantCamera>(observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 8, 4, 2, 2, 1, 3>(\n                    new ReprojectionError3<PinholeCamera>(observed_p));\n            }\n            break;\n        case Camera::PINHOLE:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 8, 4, 3, 2, 1, 3>(\n                    new ReprojectionError3<PinholeCamera>(observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 8, 4, 2, 2, 1, 3>(\n                    new ReprojectionError3<PinholeCamera>(observed_p));\n            }\n            break;\n        case Camera::MEI:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 9, 4, 3, 2, 1, 3>(\n                    new ReprojectionError3<CataCamera>(observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 9, 4, 2, 2, 1, 3>(\n                    new ReprojectionError3<CataCamera>(observed_p));\n            }\n            break;\n        case Camera::SCARAMUZZA:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, SCARAMUZZA_CAMERA_NUM_PARAMS, 4, 3, 2, 1, 3>(\n                    new ReprojectionError3<OCAMCamera>(observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, SCARAMUZZA_CAMERA_NUM_PARAMS, 4, 2, 2, 1, 3>(\n                    new ReprojectionError3<OCAMCamera>(observed_p));\n            }\n            break;\n        }\n        break;\n    case CAMERA_INTRINSICS | CAMERA_ODOMETRY_TRANSFORM | ODOMETRY_6D_POSE | POINT_3D:\n        switch (camera->modelType())\n        {\n        case Camera::KANNALA_BRANDT:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<EquidistantCamera>, 2, 8, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<EquidistantCamera>(observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 8, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<PinholeCamera>(observed_p));\n            }\n            break;\n        case Camera::PINHOLE:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 8, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<PinholeCamera>(observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 8, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<PinholeCamera>(observed_p));\n            }\n            break;\n        case Camera::MEI:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 9, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<CataCamera>(observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 9, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<CataCamera>(observed_p));\n            }\n            break;\n        case Camera::SCARAMUZZA:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, SCARAMUZZA_CAMERA_NUM_PARAMS, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<OCAMCamera>(observed_p));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, SCARAMUZZA_CAMERA_NUM_PARAMS, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<OCAMCamera>(observed_p));\n            }\n            break;\n        }\n        break;\n    }\n\n    return costFunction;\n}\n\nceres::CostFunction*\nCostFunctionFactory::generateCostFunction(const CameraConstPtr& camera,\n        const Eigen::Vector2d& observed_p,\n        const Eigen::Matrix2d& sqrtPrecisionMat,\n        int flags, bool optimize_cam_odo_z) const\n{\n    ceres::CostFunction* costFunction = 0;\n\n    std::vector<double> intrinsic_params;\n    camera->writeParameters(intrinsic_params);\n\n    switch (flags)\n    {\n    case CAMERA_ODOMETRY_TRANSFORM | ODOMETRY_6D_POSE | POINT_3D:\n        switch (camera->modelType())\n        {\n        case Camera::KANNALA_BRANDT:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<EquidistantCamera>, 2, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<EquidistantCamera>(intrinsic_params, observed_p, sqrtPrecisionMat));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<PinholeCamera>(intrinsic_params, observed_p, sqrtPrecisionMat));\n            }\n            break;\n        case Camera::PINHOLE:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<PinholeCamera>(intrinsic_params, observed_p, sqrtPrecisionMat));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<PinholeCamera>(intrinsic_params, observed_p, sqrtPrecisionMat));\n            }\n            break;\n        case Camera::MEI:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<CataCamera>(intrinsic_params, observed_p, sqrtPrecisionMat));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<CataCamera>(intrinsic_params, observed_p, sqrtPrecisionMat));\n            }\n            break;\n        case Camera::SCARAMUZZA:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<OCAMCamera>(intrinsic_params, observed_p, sqrtPrecisionMat));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<OCAMCamera>(intrinsic_params, observed_p, sqrtPrecisionMat));\n            }\n            break;\n        }\n        break;\n    case CAMERA_INTRINSICS | CAMERA_ODOMETRY_TRANSFORM | ODOMETRY_6D_POSE | POINT_3D:\n        switch (camera->modelType())\n        {\n        case Camera::KANNALA_BRANDT:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<EquidistantCamera>, 2, 8, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<EquidistantCamera>(observed_p, sqrtPrecisionMat));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 8, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<PinholeCamera>(observed_p, sqrtPrecisionMat));\n            }\n            break;\n        case Camera::PINHOLE:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 8, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<PinholeCamera>(observed_p, sqrtPrecisionMat));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 8, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<PinholeCamera>(observed_p, sqrtPrecisionMat));\n            }\n            break;\n        case Camera::MEI:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 9, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<CataCamera>(observed_p, sqrtPrecisionMat));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 9, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<CataCamera>(observed_p, sqrtPrecisionMat));\n            }\n            break;\n        case Camera::SCARAMUZZA:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, SCARAMUZZA_CAMERA_NUM_PARAMS, 4, 3, 3, 3, 3>(\n                    new ReprojectionError3<OCAMCamera>(observed_p, sqrtPrecisionMat));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, SCARAMUZZA_CAMERA_NUM_PARAMS, 4, 2, 3, 3, 3>(\n                    new ReprojectionError3<OCAMCamera>(observed_p, sqrtPrecisionMat));\n            }\n            break;\n        }\n        break;\n    }\n\n    return costFunction;\n}\n\nceres::CostFunction*\nCostFunctionFactory::generateCostFunction(const CameraConstPtr& camera,\n        const Eigen::Vector3d& odo_pos,\n        const Eigen::Vector3d& odo_att,\n        const Eigen::Vector2d& observed_p,\n        int flags, bool optimize_cam_odo_z) const\n{\n    ceres::CostFunction* costFunction = 0;\n\n    std::vector<double> intrinsic_params;\n    camera->writeParameters(intrinsic_params);\n\n    switch (flags)\n    {\n    case CAMERA_ODOMETRY_TRANSFORM | POINT_3D:\n        switch (camera->modelType())\n        {\n        case Camera::KANNALA_BRANDT:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<EquidistantCamera>, 2, 4, 3, 3>(\n                    new ReprojectionError3<EquidistantCamera>(intrinsic_params, odo_pos, odo_att, observed_p, optimize_cam_odo_z));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<EquidistantCamera>, 2, 4, 2, 3>(\n                    new ReprojectionError3<EquidistantCamera>(intrinsic_params, odo_pos, odo_att, observed_p, optimize_cam_odo_z));\n            }\n            break;\n        case Camera::PINHOLE:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 4, 3, 3>(\n                    new ReprojectionError3<PinholeCamera>(intrinsic_params, odo_pos, odo_att, observed_p, optimize_cam_odo_z));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 4, 2, 3>(\n                    new ReprojectionError3<PinholeCamera>(intrinsic_params, odo_pos, odo_att, observed_p, optimize_cam_odo_z));\n            }\n            break;\n        case Camera::MEI:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 4, 3, 3>(\n                    new ReprojectionError3<CataCamera>(intrinsic_params, odo_pos, odo_att, observed_p, optimize_cam_odo_z));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 4, 2, 3>(\n                    new ReprojectionError3<CataCamera>(intrinsic_params, odo_pos, odo_att, observed_p, optimize_cam_odo_z));\n            }\n            break;\n        case Camera::SCARAMUZZA:\n            if (optimize_cam_odo_z)\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, 4, 3, 3>(\n                    new ReprojectionError3<OCAMCamera>(intrinsic_params, odo_pos, odo_att, observed_p, optimize_cam_odo_z));\n            }\n            else\n            {\n                costFunction =\n                    new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, 4, 2, 3>(\n                    new ReprojectionError3<OCAMCamera>(intrinsic_params, odo_pos, odo_att, observed_p, optimize_cam_odo_z));\n            }\n            break;\n        }\n        break;\n    }\n\n    return costFunction;\n}\n\nceres::CostFunction*\nCostFunctionFactory::generateCostFunction(const CameraConstPtr& camera,\n        const Eigen::Quaterniond& cam_odo_q,\n        const Eigen::Vector3d& cam_odo_t,\n        const Eigen::Vector3d& odo_pos,\n        const Eigen::Vector3d& odo_att,\n        const Eigen::Vector2d& observed_p,\n        int flags) const\n{\n    ceres::CostFunction* costFunction = 0;\n\n    std::vector<double> intrinsic_params;\n    camera->writeParameters(intrinsic_params);\n\n    switch (flags)\n    {\n    case POINT_3D:\n        switch (camera->modelType())\n        {\n        case Camera::KANNALA_BRANDT:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError3<EquidistantCamera>, 2, 3>(\n                new ReprojectionError3<EquidistantCamera>(intrinsic_params, cam_odo_q, cam_odo_t, odo_pos, odo_att, observed_p));\n            break;\n        case Camera::PINHOLE:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError3<PinholeCamera>, 2, 3>(\n                new ReprojectionError3<PinholeCamera>(intrinsic_params, cam_odo_q, cam_odo_t, odo_pos, odo_att, observed_p));\n            break;\n        case Camera::MEI:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError3<CataCamera>, 2, 3>(\n                new ReprojectionError3<CataCamera>(intrinsic_params, cam_odo_q, cam_odo_t, odo_pos, odo_att, observed_p));\n            break;\n        case Camera::SCARAMUZZA:\n            costFunction =\n                new ceres::AutoDiffCostFunction<ReprojectionError3<OCAMCamera>, 2, 3>(\n                new ReprojectionError3<OCAMCamera>(intrinsic_params, cam_odo_q, cam_odo_t, odo_pos, odo_att, observed_p));\n            break;\n        }\n        break;\n    }\n\n    return costFunction;\n}\n\nceres::CostFunction*\nCostFunctionFactory::generateCostFunction(const CameraConstPtr& cameraL,\n        const CameraConstPtr& cameraR,\n        const Eigen::Vector3d& observed_P,\n        const Eigen::Vector2d& observed_p_l,\n        const Eigen::Vector2d& observed_p_r) const\n{\n    ceres::CostFunction* costFunction = 0;\n\n    if (cameraL->modelType() != cameraR->modelType())\n    {\n        return costFunction;\n    }\n\n    switch (cameraL->modelType())\n    {\n    case Camera::KANNALA_BRANDT:\n        costFunction =\n            new ceres::AutoDiffCostFunction<StereoReprojectionError<EquidistantCamera>, 4, 8, 8, 4, 3, 4, 3>(\n            new StereoReprojectionError<EquidistantCamera>(observed_P, observed_p_l, observed_p_r));\n        break;\n    case Camera::PINHOLE:\n        costFunction =\n            new ceres::AutoDiffCostFunction<StereoReprojectionError<PinholeCamera>, 4, 8, 8, 4, 3, 4, 3>(\n            new StereoReprojectionError<PinholeCamera>(observed_P, observed_p_l, observed_p_r));\n        break;\n    case Camera::MEI:\n        costFunction =\n            new ceres::AutoDiffCostFunction<StereoReprojectionError<CataCamera>, 4, 9, 9, 4, 3, 4, 3>(\n            new StereoReprojectionError<CataCamera>(observed_P, observed_p_l, observed_p_r));\n        break;\n    case Camera::SCARAMUZZA:\n        costFunction =\n            new ceres::AutoDiffCostFunction<StereoReprojectionError<OCAMCamera>, 4, SCARAMUZZA_CAMERA_NUM_PARAMS, SCARAMUZZA_CAMERA_NUM_PARAMS, 4, 3, 4, 3>(\n            new StereoReprojectionError<OCAMCamera>(observed_P, observed_p_l, observed_p_r));\n        break;\n    }\n\n    return costFunction;\n}\n\n}\n\n"
  },
  {
    "path": "src/utils/camodocal/src/camera_models/EquidistantCamera.cc",
    "content": "#include \"camodocal/camera_models/EquidistantCamera.h\"\n\n#include <cmath>\n#include <cstdio>\n#include <eigen3/Eigen/Dense>\n#include <iomanip>\n#include <iostream>\n#include <opencv2/calib3d/calib3d.hpp>\n#include <opencv2/core/eigen.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n#include \"camodocal/gpl/gpl.h\"\n\nnamespace camodocal\n{\n\nEquidistantCamera::Parameters::Parameters()\n : Camera::Parameters(KANNALA_BRANDT)\n , m_k2(0.0)\n , m_k3(0.0)\n , m_k4(0.0)\n , m_k5(0.0)\n , m_mu(0.0)\n , m_mv(0.0)\n , m_u0(0.0)\n , m_v0(0.0)\n{\n\n}\n\nEquidistantCamera::Parameters::Parameters(const std::string& cameraName,\n                                          int w, int h,\n                                          double k2, double k3, double k4, double k5,\n                                          double mu, double mv,\n                                          double u0, double v0)\n : Camera::Parameters(KANNALA_BRANDT, cameraName, w, h)\n , m_k2(k2)\n , m_k3(k3)\n , m_k4(k4)\n , m_k5(k5)\n , m_mu(mu)\n , m_mv(mv)\n , m_u0(u0)\n , m_v0(v0)\n{\n\n}\n\ndouble&\nEquidistantCamera::Parameters::k2(void)\n{\n    return m_k2;\n}\n\ndouble&\nEquidistantCamera::Parameters::k3(void)\n{\n    return m_k3;\n}\n\ndouble&\nEquidistantCamera::Parameters::k4(void)\n{\n    return m_k4;\n}\n\ndouble&\nEquidistantCamera::Parameters::k5(void)\n{\n    return m_k5;\n}\n\ndouble&\nEquidistantCamera::Parameters::mu(void)\n{\n    return m_mu;\n}\n\ndouble&\nEquidistantCamera::Parameters::mv(void)\n{\n    return m_mv;\n}\n\ndouble&\nEquidistantCamera::Parameters::u0(void)\n{\n    return m_u0;\n}\n\ndouble&\nEquidistantCamera::Parameters::v0(void)\n{\n    return m_v0;\n}\n\ndouble\nEquidistantCamera::Parameters::k2(void) const\n{\n    return m_k2;\n}\n\ndouble\nEquidistantCamera::Parameters::k3(void) const\n{\n    return m_k3;\n}\n\ndouble\nEquidistantCamera::Parameters::k4(void) const\n{\n    return m_k4;\n}\n\ndouble\nEquidistantCamera::Parameters::k5(void) const\n{\n    return m_k5;\n}\n\ndouble\nEquidistantCamera::Parameters::mu(void) const\n{\n    return m_mu;\n}\n\ndouble\nEquidistantCamera::Parameters::mv(void) const\n{\n    return m_mv;\n}\n\ndouble\nEquidistantCamera::Parameters::u0(void) const\n{\n    return m_u0;\n}\n\ndouble\nEquidistantCamera::Parameters::v0(void) const\n{\n    return m_v0;\n}\n\nbool\nEquidistantCamera::Parameters::readFromYamlFile(const std::string& filename)\n{\n    cv::FileStorage fs(filename, cv::FileStorage::READ);\n\n    if (!fs.isOpened())\n    {\n        return false;\n    }\n\n    if (!fs[\"model_type\"].isNone())\n    {\n        std::string sModelType;\n        fs[\"model_type\"] >> sModelType;\n\n        if (sModelType.compare(\"KANNALA_BRANDT\") != 0)\n        {\n            return false;\n        }\n    }\n\n    m_modelType = KANNALA_BRANDT;\n    fs[\"camera_name\"] >> m_cameraName;\n    m_imageWidth = static_cast<int>(fs[\"image_width\"]);\n    m_imageHeight = static_cast<int>(fs[\"image_height\"]);\n\n    cv::FileNode n = fs[\"projection_parameters\"];\n    m_k2 = static_cast<double>(n[\"k2\"]);\n    m_k3 = static_cast<double>(n[\"k3\"]);\n    m_k4 = static_cast<double>(n[\"k4\"]);\n    m_k5 = static_cast<double>(n[\"k5\"]);\n    m_mu = static_cast<double>(n[\"mu\"]);\n    m_mv = static_cast<double>(n[\"mv\"]);\n    m_u0 = static_cast<double>(n[\"u0\"]);\n    m_v0 = static_cast<double>(n[\"v0\"]);\n\n    return true;\n}\n\nvoid\nEquidistantCamera::Parameters::writeToYamlFile(const std::string& filename) const\n{\n    cv::FileStorage fs(filename, cv::FileStorage::WRITE);\n\n    fs << \"model_type\" << \"KANNALA_BRANDT\";\n    fs << \"camera_name\" << m_cameraName;\n    fs << \"image_width\" << m_imageWidth;\n    fs << \"image_height\" << m_imageHeight;\n\n    // projection: k2, k3, k4, k5, mu, mv, u0, v0\n    fs << \"projection_parameters\";\n    fs << \"{\" << \"k2\" << m_k2\n              << \"k3\" << m_k3\n              << \"k4\" << m_k4\n              << \"k5\" << m_k5\n              << \"mu\" << m_mu\n              << \"mv\" << m_mv\n              << \"u0\" << m_u0\n              << \"v0\" << m_v0 << \"}\";\n\n    fs.release();\n}\n\nEquidistantCamera::Parameters&\nEquidistantCamera::Parameters::operator=(const EquidistantCamera::Parameters& other)\n{\n    if (this != &other)\n    {\n        m_modelType = other.m_modelType;\n        m_cameraName = other.m_cameraName;\n        m_imageWidth = other.m_imageWidth;\n        m_imageHeight = other.m_imageHeight;\n        m_k2 = other.m_k2;\n        m_k3 = other.m_k3;\n        m_k4 = other.m_k4;\n        m_k5 = other.m_k5;\n        m_mu = other.m_mu;\n        m_mv = other.m_mv;\n        m_u0 = other.m_u0;\n        m_v0 = other.m_v0;\n    }\n\n    return *this;\n}\n\nstd::ostream&\noperator<< (std::ostream& out, const EquidistantCamera::Parameters& params)\n{\n    out << \"Camera Parameters:\" << std::endl;\n    out << \"    model_type \" << \"KANNALA_BRANDT\" << std::endl;\n    out << \"   camera_name \" << params.m_cameraName << std::endl;\n    out << \"   image_width \" << params.m_imageWidth << std::endl;\n    out << \"  image_height \" << params.m_imageHeight << std::endl;\n\n    // projection: k2, k3, k4, k5, mu, mv, u0, v0\n    out << \"Projection Parameters\" << std::endl;\n    out << \"            k2 \" << params.m_k2 << std::endl\n        << \"            k3 \" << params.m_k3 << std::endl\n        << \"            k4 \" << params.m_k4 << std::endl\n        << \"            k5 \" << params.m_k5 << std::endl\n        << \"            mu \" << params.m_mu << std::endl\n        << \"            mv \" << params.m_mv << std::endl\n        << \"            u0 \" << params.m_u0 << std::endl\n        << \"            v0 \" << params.m_v0 << std::endl;\n\n    return out;\n}\n\nEquidistantCamera::EquidistantCamera()\n : m_inv_K11(1.0)\n , m_inv_K13(0.0)\n , m_inv_K22(1.0)\n , m_inv_K23(0.0)\n{\n\n}\n\nEquidistantCamera::EquidistantCamera(const std::string& cameraName,\n                                     int imageWidth, int imageHeight,\n                                     double k2, double k3, double k4, double k5,\n                                     double mu, double mv,\n                                     double u0, double v0)\n : mParameters(cameraName, imageWidth, imageHeight,\n               k2, k3, k4, k5, mu, mv, u0, v0)\n{\n    // Inverse camera projection matrix parameters\n    m_inv_K11 = 1.0 / mParameters.mu();\n    m_inv_K13 = -mParameters.u0() / mParameters.mu();\n    m_inv_K22 = 1.0 / mParameters.mv();\n    m_inv_K23 = -mParameters.v0() / mParameters.mv();\n}\n\nEquidistantCamera::EquidistantCamera(const EquidistantCamera::Parameters& params)\n : mParameters(params)\n{\n    // Inverse camera projection matrix parameters\n    m_inv_K11 = 1.0 / mParameters.mu();\n    m_inv_K13 = -mParameters.u0() / mParameters.mu();\n    m_inv_K22 = 1.0 / mParameters.mv();\n    m_inv_K23 = -mParameters.v0() / mParameters.mv();\n}\n\nCamera::ModelType\nEquidistantCamera::modelType(void) const\n{\n    return mParameters.modelType();\n}\n\nconst std::string&\nEquidistantCamera::cameraName(void) const\n{\n    return mParameters.cameraName();\n}\n\nint\nEquidistantCamera::imageWidth(void) const\n{\n    return mParameters.imageWidth();\n}\n\nint\nEquidistantCamera::imageHeight(void) const\n{\n    return mParameters.imageHeight();\n}\n\nvoid\nEquidistantCamera::estimateIntrinsics(const cv::Size& boardSize,\n                                      const std::vector< std::vector<cv::Point3f> >& objectPoints,\n                                      const std::vector< std::vector<cv::Point2f> >& imagePoints)\n{\n    Parameters params = getParameters();\n\n    double u0 = params.imageWidth() / 2.0;\n    double v0 = params.imageHeight() / 2.0;\n\n    double minReprojErr = std::numeric_limits<double>::max();\n\n    std::vector<cv::Mat> rvecs, tvecs;\n    rvecs.assign(objectPoints.size(), cv::Mat());\n    tvecs.assign(objectPoints.size(), cv::Mat());\n\n    params.k2() = 0.0;\n    params.k3() = 0.0;\n    params.k4() = 0.0;\n    params.k5() = 0.0;\n    params.u0() = u0;\n    params.v0() = v0;\n\n    // Initialize focal length\n    // C. Hughes, P. Denny, M. Glavin, and E. Jones,\n    // Equidistant Fish-Eye Calibration and Rectification by Vanishing Point\n    // Extraction, PAMI 2010\n    // Find circles from rows of chessboard corners, and for each pair\n    // of circles, find vanishing points: v1 and v2.\n    // f = ||v1 - v2|| / PI;\n    double f0 = 0.0;\n    for (size_t i = 0; i < imagePoints.size(); ++i)\n    {\n        std::vector<Eigen::Vector2d> center(boardSize.height);\n        double radius[boardSize.height];\n        for (int r = 0; r < boardSize.height; ++r)\n        {\n            std::vector<cv::Point2d> circle;\n            for (int c = 0; c < boardSize.width; ++c)\n            {\n                circle.push_back(imagePoints.at(i).at(r * boardSize.width + c));\n            }\n\n            fitCircle(circle, center[r](0), center[r](1), radius[r]);\n        }\n\n        for (int j = 0; j < boardSize.height; ++j)\n        {\n            for (int k = j + 1; k < boardSize.height; ++k)\n            {\n                // find distance between pair of vanishing points which\n                // correspond to intersection points of 2 circles\n                std::vector<cv::Point2d> ipts;\n                ipts = intersectCircles(center[j](0), center[j](1), radius[j],\n                                        center[k](0), center[k](1), radius[k]);\n\n                if (ipts.size() < 2)\n                {\n                    continue;\n                }\n\n                double f = cv::norm(ipts.at(0) - ipts.at(1)) / M_PI;\n\n                params.mu() = f;\n                params.mv() = f;\n\n                setParameters(params);\n\n                for (size_t l = 0; l < objectPoints.size(); ++l)\n                {\n                    estimateExtrinsics(objectPoints.at(l), imagePoints.at(l), rvecs.at(l), tvecs.at(l));\n                }\n\n                double reprojErr = reprojectionError(objectPoints, imagePoints, rvecs, tvecs, cv::noArray());\n\n                if (reprojErr < minReprojErr)\n                {\n                    minReprojErr = reprojErr;\n                    f0 = f;\n                }\n            }\n        }\n    }\n\n    if (f0 <= 0.0 && minReprojErr >= std::numeric_limits<double>::max())\n    {\n        std::cout << \"[\" << params.cameraName() << \"] \"\n                  << \"# INFO: kannala-Brandt model fails with given data. \" << std::endl;\n\n        return;\n    }\n\n    params.mu() = f0;\n    params.mv() = f0;\n\n    setParameters(params);\n}\n\n/**\n * \\brief Lifts a point from the image plane to the unit sphere\n *\n * \\param p image coordinates\n * \\param P coordinates of the point on the sphere\n */\nvoid\nEquidistantCamera::liftSphere(const Eigen::Vector2d& p, Eigen::Vector3d& P) const\n{\n    liftProjective(p, P);\n}\n\n/** \n * \\brief Lifts a point from the image plane to its projective ray\n *\n * \\param p image coordinates\n * \\param P coordinates of the projective ray\n */\nvoid\nEquidistantCamera::liftProjective(const Eigen::Vector2d& p, Eigen::Vector3d& P) const\n{\n    // Lift points to normalised plane\n    Eigen::Vector2d p_u;\n    p_u << m_inv_K11 * p(0) + m_inv_K13,\n           m_inv_K22 * p(1) + m_inv_K23;\n\n    // Obtain a projective ray\n    double theta, phi;\n    backprojectSymmetric(p_u, theta, phi);\n\n    P(0) = sin(theta) * cos(phi);\n    P(1) = sin(theta) * sin(phi);\n    P(2) = cos(theta);\n}\n\n/** \n * \\brief Project a 3D point (\\a x,\\a y,\\a z) to the image plane in (\\a u,\\a v)\n *\n * \\param P 3D point coordinates\n * \\param p return value, contains the image point coordinates\n */\nvoid\nEquidistantCamera::spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p) const\n{\n    double theta = acos(P(2) / P.norm());\n    double phi = atan2(P(1), P(0));\n\n    Eigen::Vector2d p_u = r(mParameters.k2(), mParameters.k3(), mParameters.k4(), mParameters.k5(), theta) * Eigen::Vector2d(cos(phi), sin(phi));\n\n    // Apply generalised projection matrix\n    p << mParameters.mu() * p_u(0) + mParameters.u0(),\n         mParameters.mv() * p_u(1) + mParameters.v0();\n}\n\n\n/** \n * \\brief Project a 3D point to the image plane and calculate Jacobian\n *\n * \\param P 3D point coordinates\n * \\param p return value, contains the image point coordinates\n */\nvoid\nEquidistantCamera::spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p,\n                                Eigen::Matrix<double,2,3>& J) const\n{\n    double theta = acos(P(2) / P.norm());\n    double phi = atan2(P(1), P(0));\n\n    Eigen::Vector2d p_u = r(mParameters.k2(), mParameters.k3(), mParameters.k4(), mParameters.k5(), theta) * Eigen::Vector2d(cos(phi), sin(phi));\n\n    // Apply generalised projection matrix\n    p << mParameters.mu() * p_u(0) + mParameters.u0(),\n         mParameters.mv() * p_u(1) + mParameters.v0();\n}\n\n/** \n * \\brief Projects an undistorted 2D point p_u to the image plane\n *\n * \\param p_u 2D point coordinates\n * \\return image point coordinates\n */\nvoid\nEquidistantCamera::undistToPlane(const Eigen::Vector2d& p_u, Eigen::Vector2d& p) const\n{\n//    Eigen::Vector2d p_d;\n//\n//    if (m_noDistortion)\n//    {\n//        p_d = p_u;\n//    }\n//    else\n//    {\n//        // Apply distortion\n//        Eigen::Vector2d d_u;\n//        distortion(p_u, d_u);\n//        p_d = p_u + d_u;\n//    }\n//\n//    // Apply generalised projection matrix\n//    p << mParameters.gamma1() * p_d(0) + mParameters.u0(),\n//         mParameters.gamma2() * p_d(1) + mParameters.v0();\n}\n\nvoid\nEquidistantCamera::initUndistortMap(cv::Mat& map1, cv::Mat& map2, double fScale) const\n{\n    cv::Size imageSize(mParameters.imageWidth(), mParameters.imageHeight());\n\n    cv::Mat mapX = cv::Mat::zeros(imageSize, CV_32F);\n    cv::Mat mapY = cv::Mat::zeros(imageSize, CV_32F);\n\n    for (int v = 0; v < imageSize.height; ++v)\n    {\n        for (int u = 0; u < imageSize.width; ++u)\n        {\n            double mx_u = m_inv_K11 / fScale * u + m_inv_K13 / fScale;\n            double my_u = m_inv_K22 / fScale * v + m_inv_K23 / fScale;\n\n            double theta, phi;\n            backprojectSymmetric(Eigen::Vector2d(mx_u, my_u), theta, phi);\n\n            Eigen::Vector3d P;\n            P << sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta);\n\n            Eigen::Vector2d p;\n            spaceToPlane(P, p);\n\n            mapX.at<float>(v,u) = p(0);\n            mapY.at<float>(v,u) = p(1);\n        }\n    }\n\n    cv::convertMaps(mapX, mapY, map1, map2, CV_32FC1, false);\n}\n\ncv::Mat\nEquidistantCamera::initUndistortRectifyMap(cv::Mat& map1, cv::Mat& map2,\n                                           float fx, float fy,\n                                           cv::Size imageSize,\n                                           float cx, float cy,\n                                           cv::Mat rmat) const\n{\n    if (imageSize == cv::Size(0, 0))\n    {\n        imageSize = cv::Size(mParameters.imageWidth(), mParameters.imageHeight());\n    }\n\n    cv::Mat mapX = cv::Mat::zeros(imageSize.height, imageSize.width, CV_32F);\n    cv::Mat mapY = cv::Mat::zeros(imageSize.height, imageSize.width, CV_32F);\n\n    Eigen::Matrix3f K_rect;\n\n    if (cx == -1.0f && cy == -1.0f)\n    {\n        K_rect << fx, 0, imageSize.width / 2,\n                  0, fy, imageSize.height / 2,\n                  0, 0, 1;\n    }\n    else\n    {\n        K_rect << fx, 0, cx,\n                  0, fy, cy,\n                  0, 0, 1;\n    }\n\n    if (fx == -1.0f || fy == -1.0f)\n    {\n        K_rect(0,0) = mParameters.mu();\n        K_rect(1,1) = mParameters.mv();\n    }\n\n    Eigen::Matrix3f K_rect_inv = K_rect.inverse();\n\n    Eigen::Matrix3f R, R_inv;\n    cv::cv2eigen(rmat, R);\n    R_inv = R.inverse();\n\n    for (int v = 0; v < imageSize.height; ++v)\n    {\n        for (int u = 0; u < imageSize.width; ++u)\n        {\n            Eigen::Vector3f xo;\n            xo << u, v, 1;\n\n            Eigen::Vector3f uo = R_inv * K_rect_inv * xo;\n\n            Eigen::Vector2d p;\n            spaceToPlane(uo.cast<double>(), p);\n\n            mapX.at<float>(v,u) = p(0);\n            mapY.at<float>(v,u) = p(1);\n        }\n    }\n\n    cv::convertMaps(mapX, mapY, map1, map2, CV_32FC1, false);\n\n    cv::Mat K_rect_cv;\n    cv::eigen2cv(K_rect, K_rect_cv);\n    return K_rect_cv;\n}\n\nint\nEquidistantCamera::parameterCount(void) const\n{\n    return 8;\n}\n\nconst EquidistantCamera::Parameters&\nEquidistantCamera::getParameters(void) const\n{\n    return mParameters;\n}\n\nvoid\nEquidistantCamera::setParameters(const EquidistantCamera::Parameters& parameters)\n{\n    mParameters = parameters;\n\n    // Inverse camera projection matrix parameters\n    m_inv_K11 = 1.0 / mParameters.mu();\n    m_inv_K13 = -mParameters.u0() / mParameters.mu();\n    m_inv_K22 = 1.0 / mParameters.mv();\n    m_inv_K23 = -mParameters.v0() / mParameters.mv();\n}\n\nvoid\nEquidistantCamera::readParameters(const std::vector<double>& parameterVec)\n{\n    if (parameterVec.size() != parameterCount())\n    {\n        return;\n    }\n\n    Parameters params = getParameters();\n\n    params.k2() = parameterVec.at(0);\n    params.k3() = parameterVec.at(1);\n    params.k4() = parameterVec.at(2);\n    params.k5() = parameterVec.at(3);\n    params.mu() = parameterVec.at(4);\n    params.mv() = parameterVec.at(5);\n    params.u0() = parameterVec.at(6);\n    params.v0() = parameterVec.at(7);\n\n    setParameters(params);\n}\n\nvoid\nEquidistantCamera::writeParameters(std::vector<double>& parameterVec) const\n{\n    parameterVec.resize(parameterCount());\n    parameterVec.at(0) = mParameters.k2();\n    parameterVec.at(1) = mParameters.k3();\n    parameterVec.at(2) = mParameters.k4();\n    parameterVec.at(3) = mParameters.k5();\n    parameterVec.at(4) = mParameters.mu();\n    parameterVec.at(5) = mParameters.mv();\n    parameterVec.at(6) = mParameters.u0();\n    parameterVec.at(7) = mParameters.v0();\n}\n\nvoid\nEquidistantCamera::writeParametersToYamlFile(const std::string& filename) const\n{\n    mParameters.writeToYamlFile(filename);\n}\n\nstd::string\nEquidistantCamera::parametersToString(void) const\n{\n    std::ostringstream oss;\n    oss << mParameters;\n\n    return oss.str();\n}\n\nvoid\nEquidistantCamera::fitOddPoly(const std::vector<double>& x, const std::vector<double>& y,\n                              int n, std::vector<double>& coeffs) const\n{\n    std::vector<int> pows;\n    for (int i = 1; i <= n; i += 2)\n    {\n        pows.push_back(i);\n    }\n\n    Eigen::MatrixXd X(x.size(), pows.size());\n    Eigen::MatrixXd Y(y.size(), 1);\n    for (size_t i = 0; i < x.size(); ++i)\n    {\n        for (size_t j = 0; j < pows.size(); ++j)\n        {\n            X(i,j) = pow(x.at(i), pows.at(j));\n        }\n        Y(i,0) = y.at(i);\n    }\n\n    Eigen::MatrixXd A = (X.transpose() * X).inverse() * X.transpose() * Y;\n\n    coeffs.resize(A.rows());\n    for (int i = 0; i < A.rows(); ++i)\n    {\n        coeffs.at(i) = A(i,0);\n    }\n}\n\nvoid\nEquidistantCamera::backprojectSymmetric(const Eigen::Vector2d& p_u,\n                                        double& theta, double& phi) const\n{\n    double tol = 1e-10;\n    double p_u_norm = p_u.norm();\n\n    if (p_u_norm < 1e-10)\n    {\n        phi = 0.0;\n    }\n    else\n    {\n        phi = atan2(p_u(1), p_u(0));\n    }\n\n    int npow = 9;\n    if (mParameters.k5() == 0.0)\n    {\n        npow -= 2;\n    }\n    if (mParameters.k4() == 0.0)\n    {\n        npow -= 2;\n    }\n    if (mParameters.k3() == 0.0)\n    {\n        npow -= 2;\n    }\n    if (mParameters.k2() == 0.0)\n    {\n        npow -= 2;\n    }\n\n    Eigen::MatrixXd coeffs(npow + 1, 1);\n    coeffs.setZero();\n    coeffs(0) = -p_u_norm;\n    coeffs(1) = 1.0;\n\n    if (npow >= 3)\n    {\n        coeffs(3) = mParameters.k2();\n    }\n    if (npow >= 5)\n    {\n        coeffs(5) = mParameters.k3();\n    }\n    if (npow >= 7)\n    {\n        coeffs(7) = mParameters.k4();\n    }\n    if (npow >= 9)\n    {\n        coeffs(9) = mParameters.k5();\n    }\n\n    if (npow == 1)\n    {\n        theta = p_u_norm;\n    }\n    else\n    {\n        // Get eigenvalues of companion matrix corresponding to polynomial.\n        // Eigenvalues correspond to roots of polynomial.\n        Eigen::MatrixXd A(npow, npow);\n        A.setZero();\n        A.block(1, 0, npow - 1, npow - 1).setIdentity();\n        A.col(npow - 1) = - coeffs.block(0, 0, npow, 1) / coeffs(npow);\n\n        Eigen::EigenSolver<Eigen::MatrixXd> es(A);\n        Eigen::MatrixXcd eigval = es.eigenvalues();\n\n        std::vector<double> thetas;\n        for (int i = 0; i < eigval.rows(); ++i)\n        {\n            if (fabs(eigval(i).imag()) > tol)\n            {\n                continue;\n            }\n\n            double t = eigval(i).real();\n\n            if (t < -tol)\n            {\n                continue;\n            }\n            else if (t < 0.0)\n            {\n                t = 0.0;\n            }\n\n            thetas.push_back(t);\n        }\n\n        if (thetas.empty())\n        {\n            theta = p_u_norm;\n        }\n        else\n        {\n            theta = *std::min_element(thetas.begin(), thetas.end());\n        }\n    }\n}\n\n}\n"
  },
  {
    "path": "src/utils/camodocal/src/camera_models/PinholeCamera.cc",
    "content": "#include \"camodocal/camera_models/PinholeCamera.h\"\n\n#include <cmath>\n#include <cstdio>\n#include <eigen3/Eigen/Dense>\n#include <iomanip>\n#include <opencv2/calib3d/calib3d.hpp>\n#include <opencv2/core/eigen.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n#include \"camodocal/gpl/gpl.h\"\n\nnamespace camodocal\n{\n\nPinholeCamera::Parameters::Parameters()\n : Camera::Parameters(PINHOLE)\n , m_k1(0.0)\n , m_k2(0.0)\n , m_p1(0.0)\n , m_p2(0.0)\n , m_fx(0.0)\n , m_fy(0.0)\n , m_cx(0.0)\n , m_cy(0.0)\n{\n\n}\n\nPinholeCamera::Parameters::Parameters(const std::string& cameraName,\n                                      int w, int h,\n                                      double k1, double k2,\n                                      double p1, double p2,\n                                      double fx, double fy,\n                                      double cx, double cy)\n : Camera::Parameters(PINHOLE, cameraName, w, h)\n , m_k1(k1)\n , m_k2(k2)\n , m_p1(p1)\n , m_p2(p2)\n , m_fx(fx)\n , m_fy(fy)\n , m_cx(cx)\n , m_cy(cy)\n{\n}\n\ndouble&\nPinholeCamera::Parameters::k1(void)\n{\n    return m_k1;\n}\n\ndouble&\nPinholeCamera::Parameters::k2(void)\n{\n    return m_k2;\n}\n\ndouble&\nPinholeCamera::Parameters::p1(void)\n{\n    return m_p1;\n}\n\ndouble&\nPinholeCamera::Parameters::p2(void)\n{\n    return m_p2;\n}\n\ndouble&\nPinholeCamera::Parameters::fx(void)\n{\n    return m_fx;\n}\n\ndouble&\nPinholeCamera::Parameters::fy(void)\n{\n    return m_fy;\n}\n\ndouble&\nPinholeCamera::Parameters::cx(void)\n{\n    return m_cx;\n}\n\ndouble&\nPinholeCamera::Parameters::cy(void)\n{\n    return m_cy;\n}\n\ndouble\nPinholeCamera::Parameters::k1(void) const\n{\n    return m_k1;\n}\n\ndouble\nPinholeCamera::Parameters::k2(void) const\n{\n    return m_k2;\n}\n\ndouble\nPinholeCamera::Parameters::p1(void) const\n{\n    return m_p1;\n}\n\ndouble\nPinholeCamera::Parameters::p2(void) const\n{\n    return m_p2;\n}\n\ndouble\nPinholeCamera::Parameters::fx(void) const\n{\n    return m_fx;\n}\n\ndouble\nPinholeCamera::Parameters::fy(void) const\n{\n    return m_fy;\n}\n\ndouble\nPinholeCamera::Parameters::cx(void) const\n{\n    return m_cx;\n}\n\ndouble\nPinholeCamera::Parameters::cy(void) const\n{\n    return m_cy;\n}\n\nbool\nPinholeCamera::Parameters::readFromYamlFile(const std::string& filename)\n{\n    cv::FileStorage fs(filename, cv::FileStorage::READ);\n\n    if (!fs.isOpened())\n    {\n        return false;\n    }\n\n    if (!fs[\"model_type\"].isNone())\n    {\n        std::string sModelType;\n        fs[\"model_type\"] >> sModelType;\n\n        if (sModelType.compare(\"PINHOLE\") != 0)\n        {\n            return false;\n        }\n    }\n\n    m_modelType = PINHOLE;\n    fs[\"camera_name\"] >> m_cameraName;\n    m_imageWidth = static_cast<int>(fs[\"image_width\"]);\n    m_imageHeight = static_cast<int>(fs[\"image_height\"]);\n\n    cv::FileNode n = fs[\"distortion_parameters\"];\n    m_k1 = static_cast<double>(n[\"k1\"]);\n    m_k2 = static_cast<double>(n[\"k2\"]);\n    m_p1 = static_cast<double>(n[\"p1\"]);\n    m_p2 = static_cast<double>(n[\"p2\"]);\n\n    n = fs[\"projection_parameters\"];\n    m_fx = static_cast<double>(n[\"fx\"]);\n    m_fy = static_cast<double>(n[\"fy\"]);\n    m_cx = static_cast<double>(n[\"cx\"]);\n    m_cy = static_cast<double>(n[\"cy\"]);\n\n    return true;\n}\n\nvoid\nPinholeCamera::Parameters::writeToYamlFile(const std::string& filename) const\n{\n    cv::FileStorage fs(filename, cv::FileStorage::WRITE);\n\n    fs << \"model_type\" << \"PINHOLE\";\n    fs << \"camera_name\" << m_cameraName;\n    fs << \"image_width\" << m_imageWidth;\n    fs << \"image_height\" << m_imageHeight;\n\n    // radial distortion: k1, k2\n    // tangential distortion: p1, p2\n    fs << \"distortion_parameters\";\n    fs << \"{\" << \"k1\" << m_k1\n              << \"k2\" << m_k2\n              << \"p1\" << m_p1\n              << \"p2\" << m_p2 << \"}\";\n\n    // projection: fx, fy, cx, cy\n    fs << \"projection_parameters\";\n    fs << \"{\" << \"fx\" << m_fx\n              << \"fy\" << m_fy\n              << \"cx\" << m_cx\n              << \"cy\" << m_cy << \"}\";\n\n    fs.release();\n}\n\nPinholeCamera::Parameters&\nPinholeCamera::Parameters::operator=(const PinholeCamera::Parameters& other)\n{\n    if (this != &other)\n    {\n        m_modelType = other.m_modelType;\n        m_cameraName = other.m_cameraName;\n        m_imageWidth = other.m_imageWidth;\n        m_imageHeight = other.m_imageHeight;\n        m_k1 = other.m_k1;\n        m_k2 = other.m_k2;\n        m_p1 = other.m_p1;\n        m_p2 = other.m_p2;\n        m_fx = other.m_fx;\n        m_fy = other.m_fy;\n        m_cx = other.m_cx;\n        m_cy = other.m_cy;\n    }\n\n    return *this;\n}\n\nstd::ostream&\noperator<< (std::ostream& out, const PinholeCamera::Parameters& params)\n{\n    out << \"Camera Parameters:\" << std::endl;\n    out << \"    model_type \" << \"PINHOLE\" << std::endl;\n    out << \"   camera_name \" << params.m_cameraName << std::endl;\n    out << \"   image_width \" << params.m_imageWidth << std::endl;\n    out << \"  image_height \" << params.m_imageHeight << std::endl;\n\n    // radial distortion: k1, k2\n    // tangential distortion: p1, p2\n    out << \"Distortion Parameters\" << std::endl;\n    out << \"            k1 \" << params.m_k1 << std::endl\n        << \"            k2 \" << params.m_k2 << std::endl\n        << \"            p1 \" << params.m_p1 << std::endl\n        << \"            p2 \" << params.m_p2 << std::endl;\n\n    // projection: fx, fy, cx, cy\n    out << \"Projection Parameters\" << std::endl;\n    out << \"            fx \" << params.m_fx << std::endl\n        << \"            fy \" << params.m_fy << std::endl\n        << \"            cx \" << params.m_cx << std::endl\n        << \"            cy \" << params.m_cy << std::endl;\n\n    return out;\n}\n\nPinholeCamera::PinholeCamera()\n : m_inv_K11(1.0)\n , m_inv_K13(0.0)\n , m_inv_K22(1.0)\n , m_inv_K23(0.0)\n , m_noDistortion(true)\n{\n\n}\n\nPinholeCamera::PinholeCamera(const std::string& cameraName,\n                             int imageWidth, int imageHeight,\n                             double k1, double k2, double p1, double p2,\n                             double fx, double fy, double cx, double cy)\n : mParameters(cameraName, imageWidth, imageHeight,\n               k1, k2, p1, p2, fx, fy, cx, cy)\n{\n    if ((mParameters.k1() == 0.0) &&\n        (mParameters.k2() == 0.0) &&\n        (mParameters.p1() == 0.0) &&\n        (mParameters.p2() == 0.0))\n    {\n        m_noDistortion = true;\n    }\n    else\n    {\n        m_noDistortion = false;\n    }\n\n    // Inverse camera projection matrix parameters\n    m_inv_K11 = 1.0 / mParameters.fx();\n    m_inv_K13 = -mParameters.cx() / mParameters.fx();\n    m_inv_K22 = 1.0 / mParameters.fy();\n    m_inv_K23 = -mParameters.cy() / mParameters.fy();\n}\n\nPinholeCamera::PinholeCamera(const PinholeCamera::Parameters& params)\n : mParameters(params)\n{\n    if ((mParameters.k1() == 0.0) &&\n        (mParameters.k2() == 0.0) &&\n        (mParameters.p1() == 0.0) &&\n        (mParameters.p2() == 0.0))\n    {\n        m_noDistortion = true;\n    }\n    else\n    {\n        m_noDistortion = false;\n    }\n\n    // Inverse camera projection matrix parameters\n    m_inv_K11 = 1.0 / mParameters.fx();\n    m_inv_K13 = -mParameters.cx() / mParameters.fx();\n    m_inv_K22 = 1.0 / mParameters.fy();\n    m_inv_K23 = -mParameters.cy() / mParameters.fy();\n}\n\nCamera::ModelType\nPinholeCamera::modelType(void) const\n{\n    return mParameters.modelType();\n}\n\nconst std::string&\nPinholeCamera::cameraName(void) const\n{\n    return mParameters.cameraName();\n}\n\nint\nPinholeCamera::imageWidth(void) const\n{\n    return mParameters.imageWidth();\n}\n\nint\nPinholeCamera::imageHeight(void) const\n{\n    return mParameters.imageHeight();\n}\n\nvoid\nPinholeCamera::estimateIntrinsics(const cv::Size& boardSize,\n                                  const std::vector< std::vector<cv::Point3f> >& objectPoints,\n                                  const std::vector< std::vector<cv::Point2f> >& imagePoints)\n{\n    // Z. Zhang, A Flexible New Technique for Camera Calibration, PAMI 2000\n\n    Parameters params = getParameters();\n\n    params.k1() = 0.0;\n    params.k2() = 0.0;\n    params.p1() = 0.0;\n    params.p2() = 0.0;\n\n    double cx = params.imageWidth() / 2.0;\n    double cy = params.imageHeight() / 2.0;\n    params.cx() = cx;\n    params.cy() = cy;\n\n    size_t nImages = imagePoints.size();\n\n    cv::Mat A(nImages * 2, 2, CV_64F);\n    cv::Mat b(nImages * 2, 1, CV_64F);\n\n    for (size_t i = 0; i < nImages; ++i)\n    {\n        const std::vector<cv::Point3f>& oPoints = objectPoints.at(i);\n\n        std::vector<cv::Point2f> M(oPoints.size());\n        for (size_t j = 0; j < M.size(); ++j)\n        {\n            M.at(j) = cv::Point2f(oPoints.at(j).x, oPoints.at(j).y);\n        }\n\n        cv::Mat H = cv::findHomography(M, imagePoints.at(i));\n\n        H.at<double>(0,0) -= H.at<double>(2,0) * cx;\n        H.at<double>(0,1) -= H.at<double>(2,1) * cx;\n        H.at<double>(0,2) -= H.at<double>(2,2) * cx;\n        H.at<double>(1,0) -= H.at<double>(2,0) * cy;\n        H.at<double>(1,1) -= H.at<double>(2,1) * cy;\n        H.at<double>(1,2) -= H.at<double>(2,2) * cy;\n\n        double h[3], v[3], d1[3], d2[3];\n        double n[4] = {0,0,0,0};\n\n        for (int j = 0; j < 3; ++j)\n        {\n            double t0 = H.at<double>(j,0);\n            double t1 = H.at<double>(j,1);\n            h[j] = t0; v[j] = t1;\n            d1[j] = (t0 + t1) * 0.5;\n            d2[j] = (t0 - t1) * 0.5;\n            n[0] += t0 * t0; n[1] += t1 * t1;\n            n[2] += d1[j] * d1[j]; n[3] += d2[j] * d2[j];\n        }\n\n        for (int j = 0; j < 4; ++j)\n        {\n            n[j] = 1.0 / sqrt(n[j]);\n        }\n\n        for (int j = 0; j < 3; ++j)\n        {\n            h[j] *= n[0]; v[j] *= n[1];\n            d1[j] *= n[2]; d2[j] *= n[3];\n        }\n\n        A.at<double>(i * 2, 0) = h[0] * v[0];\n        A.at<double>(i * 2, 1) = h[1] * v[1];\n        A.at<double>(i * 2 + 1, 0) = d1[0] * d2[0];\n        A.at<double>(i * 2 + 1, 1) = d1[1] * d2[1];\n        b.at<double>(i * 2, 0) = -h[2] * v[2];\n        b.at<double>(i * 2 + 1, 0) = -d1[2] * d2[2];\n    }\n\n    cv::Mat f(2, 1, CV_64F);\n    cv::solve(A, b, f, cv::DECOMP_NORMAL | cv::DECOMP_LU);\n\n    params.fx() = sqrt(fabs(1.0 / f.at<double>(0)));\n    params.fy() = sqrt(fabs(1.0 / f.at<double>(1)));\n\n    setParameters(params);\n}\n\n/**\n * \\brief Lifts a point from the image plane to the unit sphere\n *\n * \\param p image coordinates\n * \\param P coordinates of the point on the sphere\n */\nvoid\nPinholeCamera::liftSphere(const Eigen::Vector2d& p, Eigen::Vector3d& P) const\n{\n    liftProjective(p, P);\n\n    P.normalize();\n}\n\n/**\n * \\brief Lifts a point from the image plane to its projective ray\n *\n * \\param p image coordinates\n * \\param P coordinates of the projective ray\n */\nvoid\nPinholeCamera::liftProjective(const Eigen::Vector2d& p, Eigen::Vector3d& P) const\n{\n    double mx_d, my_d,mx2_d, mxy_d, my2_d, mx_u, my_u;\n    double rho2_d, rho4_d, radDist_d, Dx_d, Dy_d, inv_denom_d;\n    //double lambda;\n\n    // Lift points to normalised plane\n    mx_d = m_inv_K11 * p(0) + m_inv_K13;\n    my_d = m_inv_K22 * p(1) + m_inv_K23;\n\n    if (m_noDistortion)\n    {\n        mx_u = mx_d;\n        my_u = my_d;\n    }\n    else\n    {\n        if (0)\n        {\n            double k1 = mParameters.k1();\n            double k2 = mParameters.k2();\n            double p1 = mParameters.p1();\n            double p2 = mParameters.p2();\n\n            // Apply inverse distortion model\n            // proposed by Heikkila\n            mx2_d = mx_d*mx_d;\n            my2_d = my_d*my_d;\n            mxy_d = mx_d*my_d;\n            rho2_d = mx2_d+my2_d;\n            rho4_d = rho2_d*rho2_d;\n            radDist_d = k1*rho2_d+k2*rho4_d;\n            Dx_d = mx_d*radDist_d + p2*(rho2_d+2*mx2_d) + 2*p1*mxy_d;\n            Dy_d = my_d*radDist_d + p1*(rho2_d+2*my2_d) + 2*p2*mxy_d;\n            inv_denom_d = 1/(1+4*k1*rho2_d+6*k2*rho4_d+8*p1*my_d+8*p2*mx_d);\n\n            mx_u = mx_d - inv_denom_d*Dx_d;\n            my_u = my_d - inv_denom_d*Dy_d;\n        }\n        else\n        {\n            // Recursive distortion model\n            int n = 8;\n            Eigen::Vector2d d_u;\n            distortion(Eigen::Vector2d(mx_d, my_d), d_u);\n            // Approximate value\n            mx_u = mx_d - d_u(0);\n            my_u = my_d - d_u(1);\n\n            for (int i = 1; i < n; ++i)\n            {\n                distortion(Eigen::Vector2d(mx_u, my_u), d_u);\n                mx_u = mx_d - d_u(0);\n                my_u = my_d - d_u(1);\n            }\n        }\n    }\n\n    // Obtain a projective ray\n    P << mx_u, my_u, 1.0;\n}\n\n\n/**\n * \\brief Project a 3D point (\\a x,\\a y,\\a z) to the image plane in (\\a u,\\a v)\n *\n * \\param P 3D point coordinates\n * \\param p return value, contains the image point coordinates\n */\nvoid\nPinholeCamera::spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p) const\n{\n    Eigen::Vector2d p_u, p_d;\n\n    // Project points to the normalised plane\n    p_u << P(0) / P(2), P(1) / P(2);\n\n    if (m_noDistortion)\n    {\n        p_d = p_u;\n    }\n    else\n    {\n        // Apply distortion\n        Eigen::Vector2d d_u;\n        distortion(p_u, d_u);\n        p_d = p_u + d_u;\n    }\n\n    // Apply generalised projection matrix\n    p << mParameters.fx() * p_d(0) + mParameters.cx(),\n         mParameters.fy() * p_d(1) + mParameters.cy();\n}\n\n#if 0\n/**\n * \\brief Project a 3D point to the image plane and calculate Jacobian\n *\n * \\param P 3D point coordinates\n * \\param p return value, contains the image point coordinates\n */\nvoid\nPinholeCamera::spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p,\n                            Eigen::Matrix<double,2,3>& J) const\n{\n    Eigen::Vector2d p_u, p_d;\n    double norm, inv_denom;\n    double dxdmx, dydmx, dxdmy, dydmy;\n\n    norm = P.norm();\n    // Project points to the normalised plane\n    inv_denom = 1.0 / P(2);\n    p_u << inv_denom * P(0), inv_denom * P(1);\n\n    // Calculate jacobian\n    double dudx = inv_denom;\n    double dvdx = 0.0;\n    double dudy = 0.0;\n    double dvdy = inv_denom;\n    inv_denom = - inv_denom * inv_denom;\n    double dudz = P(0) * inv_denom;\n    double dvdz = P(1) * inv_denom;\n\n    if (m_noDistortion)\n    {\n        p_d = p_u;\n    }\n    else\n    {\n        // Apply distortion\n        Eigen::Vector2d d_u;\n        distortion(p_u, d_u);\n        p_d = p_u + d_u;\n    }\n\n    double fx = mParameters.fx();\n    double fy = mParameters.fy();\n\n    // Make the product of the jacobians\n    // and add projection matrix jacobian\n    inv_denom = fx * (dudx * dxdmx + dvdx * dxdmy); // reuse\n    dvdx = fy * (dudx * dydmx + dvdx * dydmy);\n    dudx = inv_denom;\n\n    inv_denom = fx * (dudy * dxdmx + dvdy * dxdmy); // reuse\n    dvdy = fy * (dudy * dydmx + dvdy * dydmy);\n    dudy = inv_denom;\n\n    inv_denom = fx * (dudz * dxdmx + dvdz * dxdmy); // reuse\n    dvdz = fy * (dudz * dydmx + dvdz * dydmy);\n    dudz = inv_denom;\n\n    // Apply generalised projection matrix\n    p << fx * p_d(0) + mParameters.cx(),\n         fy * p_d(1) + mParameters.cy();\n\n    J << dudx, dudy, dudz,\n         dvdx, dvdy, dvdz;\n}\n#endif\n\n/**\n * \\brief Projects an undistorted 2D point p_u to the image plane\n *\n * \\param p_u 2D point coordinates\n * \\return image point coordinates\n */\nvoid\nPinholeCamera::undistToPlane(const Eigen::Vector2d& p_u, Eigen::Vector2d& p) const\n{\n    Eigen::Vector2d p_d;\n\n    if (m_noDistortion)\n    {\n        p_d = p_u;\n    }\n    else\n    {\n        // Apply distortion\n        Eigen::Vector2d d_u;\n        distortion(p_u, d_u);\n        p_d = p_u + d_u;\n    }\n\n    // Apply generalised projection matrix\n    p << mParameters.fx() * p_d(0) + mParameters.cx(),\n         mParameters.fy() * p_d(1) + mParameters.cy();\n}\n\n/**\n * \\brief Apply distortion to input point (from the normalised plane)\n *\n * \\param p_u undistorted coordinates of point on the normalised plane\n * \\return to obtain the distorted point: p_d = p_u + d_u\n */\nvoid\nPinholeCamera::distortion(const Eigen::Vector2d& p_u, Eigen::Vector2d& d_u) const\n{\n    double k1 = mParameters.k1();\n    double k2 = mParameters.k2();\n    double p1 = mParameters.p1();\n    double p2 = mParameters.p2();\n\n    double mx2_u, my2_u, mxy_u, rho2_u, rad_dist_u;\n\n    mx2_u = p_u(0) * p_u(0);\n    my2_u = p_u(1) * p_u(1);\n    mxy_u = p_u(0) * p_u(1);\n    rho2_u = mx2_u + my2_u;\n    rad_dist_u = k1 * rho2_u + k2 * rho2_u * rho2_u;\n    d_u << p_u(0) * rad_dist_u + 2.0 * p1 * mxy_u + p2 * (rho2_u + 2.0 * mx2_u),\n           p_u(1) * rad_dist_u + 2.0 * p2 * mxy_u + p1 * (rho2_u + 2.0 * my2_u);\n}\n\n/**\n * \\brief Apply distortion to input point (from the normalised plane)\n *        and calculate Jacobian\n *\n * \\param p_u undistorted coordinates of point on the normalised plane\n * \\return to obtain the distorted point: p_d = p_u + d_u\n */\nvoid\nPinholeCamera::distortion(const Eigen::Vector2d& p_u, Eigen::Vector2d& d_u,\n                          Eigen::Matrix2d& J) const\n{\n    double k1 = mParameters.k1();\n    double k2 = mParameters.k2();\n    double p1 = mParameters.p1();\n    double p2 = mParameters.p2();\n\n    double mx2_u, my2_u, mxy_u, rho2_u, rad_dist_u;\n\n    mx2_u = p_u(0) * p_u(0);\n    my2_u = p_u(1) * p_u(1);\n    mxy_u = p_u(0) * p_u(1);\n    rho2_u = mx2_u + my2_u;\n    rad_dist_u = k1 * rho2_u + k2 * rho2_u * rho2_u;\n    d_u << p_u(0) * rad_dist_u + 2.0 * p1 * mxy_u + p2 * (rho2_u + 2.0 * mx2_u),\n           p_u(1) * rad_dist_u + 2.0 * p2 * mxy_u + p1 * (rho2_u + 2.0 * my2_u);\n\n    double dxdmx = 1.0 + rad_dist_u + k1 * 2.0 * mx2_u + k2 * rho2_u * 4.0 * mx2_u + 2.0 * p1 * p_u(1) + 6.0 * p2 * p_u(0);\n    double dydmx = k1 * 2.0 * p_u(0) * p_u(1) + k2 * 4.0 * rho2_u * p_u(0) * p_u(1) + p1 * 2.0 * p_u(0) + 2.0 * p2 * p_u(1);\n    double dxdmy = dydmx;\n    double dydmy = 1.0 + rad_dist_u + k1 * 2.0 * my2_u + k2 * rho2_u * 4.0 * my2_u + 6.0 * p1 * p_u(1) + 2.0 * p2 * p_u(0);\n\n    J << dxdmx, dxdmy,\n         dydmx, dydmy;\n}\n\nvoid\nPinholeCamera::initUndistortMap(cv::Mat& map1, cv::Mat& map2, double fScale) const\n{\n    cv::Size imageSize(mParameters.imageWidth(), mParameters.imageHeight());\n\n    cv::Mat mapX = cv::Mat::zeros(imageSize, CV_32F);\n    cv::Mat mapY = cv::Mat::zeros(imageSize, CV_32F);\n\n    for (int v = 0; v < imageSize.height; ++v)\n    {\n        for (int u = 0; u < imageSize.width; ++u)\n        {\n            double mx_u = m_inv_K11 / fScale * u + m_inv_K13 / fScale;\n            double my_u = m_inv_K22 / fScale * v + m_inv_K23 / fScale;\n\n            Eigen::Vector3d P;\n            P << mx_u, my_u, 1.0;\n\n            Eigen::Vector2d p;\n            spaceToPlane(P, p);\n\n            mapX.at<float>(v,u) = p(0);\n            mapY.at<float>(v,u) = p(1);\n        }\n    }\n\n    cv::convertMaps(mapX, mapY, map1, map2, CV_32FC1, false);\n}\n\ncv::Mat\nPinholeCamera::initUndistortRectifyMap(cv::Mat& map1, cv::Mat& map2,\n                                       float fx, float fy,\n                                       cv::Size imageSize,\n                                       float cx, float cy,\n                                       cv::Mat rmat) const\n{\n    if (imageSize == cv::Size(0, 0))\n    {\n        imageSize = cv::Size(mParameters.imageWidth(), mParameters.imageHeight());\n    }\n\n    cv::Mat mapX = cv::Mat::zeros(imageSize.height, imageSize.width, CV_32F);\n    cv::Mat mapY = cv::Mat::zeros(imageSize.height, imageSize.width, CV_32F);\n\n    Eigen::Matrix3f R, R_inv;\n    cv::cv2eigen(rmat, R);\n    R_inv = R.inverse();\n\n    // assume no skew\n    Eigen::Matrix3f K_rect;\n\n    if (cx == -1.0f || cy == -1.0f)\n    {\n        K_rect << fx, 0, imageSize.width / 2,\n                  0, fy, imageSize.height / 2,\n                  0, 0, 1;\n    }\n    else\n    {\n        K_rect << fx, 0, cx,\n                  0, fy, cy,\n                  0, 0, 1;\n    }\n\n    if (fx == -1.0f || fy == -1.0f)\n    {\n        K_rect(0,0) = mParameters.fx();\n        K_rect(1,1) = mParameters.fy();\n    }\n\n    Eigen::Matrix3f K_rect_inv = K_rect.inverse();\n\n    for (int v = 0; v < imageSize.height; ++v)\n    {\n        for (int u = 0; u < imageSize.width; ++u)\n        {\n            Eigen::Vector3f xo;\n            xo << u, v, 1;\n\n            Eigen::Vector3f uo = R_inv * K_rect_inv * xo;\n\n            Eigen::Vector2d p;\n            spaceToPlane(uo.cast<double>(), p);\n\n            mapX.at<float>(v,u) = p(0);\n            mapY.at<float>(v,u) = p(1);\n        }\n    }\n\n    cv::convertMaps(mapX, mapY, map1, map2, CV_32FC1, false);\n\n    cv::Mat K_rect_cv;\n    cv::eigen2cv(K_rect, K_rect_cv);\n    return K_rect_cv;\n}\n\nint\nPinholeCamera::parameterCount(void) const\n{\n    return 8;\n}\n\nconst PinholeCamera::Parameters&\nPinholeCamera::getParameters(void) const\n{\n    return mParameters;\n}\n\nvoid\nPinholeCamera::setParameters(const PinholeCamera::Parameters& parameters)\n{\n    mParameters = parameters;\n\n    if ((mParameters.k1() == 0.0) &&\n        (mParameters.k2() == 0.0) &&\n        (mParameters.p1() == 0.0) &&\n        (mParameters.p2() == 0.0))\n    {\n        m_noDistortion = true;\n    }\n    else\n    {\n        m_noDistortion = false;\n    }\n\n    m_inv_K11 = 1.0 / mParameters.fx();\n    m_inv_K13 = -mParameters.cx() / mParameters.fx();\n    m_inv_K22 = 1.0 / mParameters.fy();\n    m_inv_K23 = -mParameters.cy() / mParameters.fy();\n}\n\nvoid\nPinholeCamera::readParameters(const std::vector<double>& parameterVec)\n{\n    if ((int)parameterVec.size() != parameterCount())\n    {\n        return;\n    }\n\n    Parameters params = getParameters();\n\n    params.k1() = parameterVec.at(0);\n    params.k2() = parameterVec.at(1);\n    params.p1() = parameterVec.at(2);\n    params.p2() = parameterVec.at(3);\n    params.fx() = parameterVec.at(4);\n    params.fy() = parameterVec.at(5);\n    params.cx() = parameterVec.at(6);\n    params.cy() = parameterVec.at(7);\n\n    setParameters(params);\n}\n\nvoid\nPinholeCamera::writeParameters(std::vector<double>& parameterVec) const\n{\n    parameterVec.resize(parameterCount());\n    parameterVec.at(0) = mParameters.k1();\n    parameterVec.at(1) = mParameters.k2();\n    parameterVec.at(2) = mParameters.p1();\n    parameterVec.at(3) = mParameters.p2();\n    parameterVec.at(4) = mParameters.fx();\n    parameterVec.at(5) = mParameters.fy();\n    parameterVec.at(6) = mParameters.cx();\n    parameterVec.at(7) = mParameters.cy();\n}\n\nvoid\nPinholeCamera::writeParametersToYamlFile(const std::string& filename) const\n{\n    mParameters.writeToYamlFile(filename);\n}\n\nstd::string\nPinholeCamera::parametersToString(void) const\n{\n    std::ostringstream oss;\n    oss << mParameters;\n\n    return oss.str();\n}\n\n}\n"
  },
  {
    "path": "src/utils/camodocal/src/camera_models/ScaramuzzaCamera.cc",
    "content": "#include \"camodocal/camera_models/ScaramuzzaCamera.h\"\n\n#include <cmath>\n#include <cstdio>\n#include <eigen3/Eigen/Dense>\n#include <eigen3/Eigen/SVD>\n#include <iomanip>\n#include <iostream>\n#include <opencv2/calib3d/calib3d.hpp>\n#include <opencv2/core/eigen.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n#include <boost/lexical_cast.hpp>\n#include <boost/algorithm/string.hpp>\n\n#include \"camodocal/gpl/gpl.h\"\n\n\nEigen::VectorXd polyfit(Eigen::VectorXd& xVec, Eigen::VectorXd& yVec, int poly_order) {\n    assert(poly_order > 0);\n    assert(xVec.size() > poly_order);\n    assert(xVec.size() == yVec.size());\n\n    Eigen::MatrixXd A(xVec.size(), poly_order+1);\n    Eigen::VectorXd B(xVec.size());\n\n    for(int i = 0; i < xVec.size(); ++i) {\n        const double x = xVec(i);\n        const double y = yVec(i);\n\n        double x_pow_k = 1.0;\n\n        for(int k=0; k<=poly_order; ++k) {\n            A(i,k) = x_pow_k;\n            x_pow_k *= x;\n        }\n\n        B(i) = y;\n    }\n\n    Eigen::JacobiSVD<Eigen::MatrixXd> svd(A, Eigen::ComputeThinU | Eigen::ComputeThinV);\n    Eigen::VectorXd x = svd.solve(B);\n\n    return x;\n}\n\nnamespace camodocal\n{\n\nOCAMCamera::Parameters::Parameters()\n : Camera::Parameters(SCARAMUZZA)\n , m_C(0.0)\n , m_D(0.0)\n , m_E(0.0)\n , m_center_x(0.0)\n , m_center_y(0.0)\n{\n    memset(m_poly, 0, sizeof(double) * SCARAMUZZA_POLY_SIZE);\n    memset(m_inv_poly, 0, sizeof(double) * SCARAMUZZA_INV_POLY_SIZE);\n}\n\n\n\nbool\nOCAMCamera::Parameters::readFromYamlFile(const std::string& filename)\n{\n    cv::FileStorage fs(filename, cv::FileStorage::READ);\n\n    if (!fs.isOpened())\n    {\n        return false;\n    }\n\n    if (!fs[\"model_type\"].isNone())\n    {\n        std::string sModelType;\n        fs[\"model_type\"] >> sModelType;\n\n        if (!boost::iequals(sModelType, \"scaramuzza\"))\n        {\n            return false;\n        }\n    }\n\n    m_modelType = SCARAMUZZA;\n    fs[\"camera_name\"] >> m_cameraName;\n    m_imageWidth = static_cast<int>(fs[\"image_width\"]);\n    m_imageHeight = static_cast<int>(fs[\"image_height\"]);\n\n    cv::FileNode n = fs[\"poly_parameters\"];\n    for(int i=0; i < SCARAMUZZA_POLY_SIZE; i++)\n        m_poly[i] = static_cast<double>(n[std::string(\"p\") + boost::lexical_cast<std::string>(i)]);\n\n    n = fs[\"inv_poly_parameters\"];\n    for(int i=0; i < SCARAMUZZA_INV_POLY_SIZE; i++)\n        m_inv_poly[i] = static_cast<double>(n[std::string(\"p\") + boost::lexical_cast<std::string>(i)]);\n\n    n = fs[\"affine_parameters\"];\n    m_C = static_cast<double>(n[\"ac\"]);\n    m_D = static_cast<double>(n[\"ad\"]);\n    m_E = static_cast<double>(n[\"ae\"]);\n\n    m_center_x = static_cast<double>(n[\"cx\"]);\n    m_center_y = static_cast<double>(n[\"cy\"]);\n\n    return true;\n}\n\nvoid\nOCAMCamera::Parameters::writeToYamlFile(const std::string& filename) const\n{\n    cv::FileStorage fs(filename, cv::FileStorage::WRITE);\n\n    fs << \"model_type\" << \"scaramuzza\";\n    fs << \"camera_name\" << m_cameraName;\n    fs << \"image_width\" << m_imageWidth;\n    fs << \"image_height\" << m_imageHeight;\n\n    fs << \"poly_parameters\";\n    fs << \"{\";\n    for(int i=0; i < SCARAMUZZA_POLY_SIZE; i++)\n        fs << std::string(\"p\") + boost::lexical_cast<std::string>(i) << m_poly[i];\n    fs << \"}\";\n\n    fs << \"inv_poly_parameters\";\n    fs << \"{\";\n    for(int i=0; i < SCARAMUZZA_INV_POLY_SIZE; i++)\n        fs << std::string(\"p\") + boost::lexical_cast<std::string>(i) << m_inv_poly[i];\n    fs << \"}\";\n\n    fs << \"affine_parameters\";\n    fs << \"{\" << \"ac\" << m_C\n              << \"ad\" << m_D\n              << \"ae\" << m_E\n              << \"cx\" << m_center_x\n              << \"cy\" << m_center_y << \"}\";\n\n    fs.release();\n}\n\nOCAMCamera::Parameters&\nOCAMCamera::Parameters::operator=(const OCAMCamera::Parameters& other)\n{\n    if (this != &other)\n    {\n        m_modelType = other.m_modelType;\n        m_cameraName = other.m_cameraName;\n        m_imageWidth = other.m_imageWidth;\n        m_imageHeight = other.m_imageHeight;\n        m_C = other.m_C;\n        m_D = other.m_D;\n        m_E = other.m_E;\n        m_center_x = other.m_center_x;\n        m_center_y = other.m_center_y;\n\n        memcpy(m_poly, other.m_poly, sizeof(double) * SCARAMUZZA_POLY_SIZE);\n        memcpy(m_inv_poly, other.m_inv_poly, sizeof(double) * SCARAMUZZA_INV_POLY_SIZE);\n    }\n\n    return *this;\n}\n\nstd::ostream&\noperator<< (std::ostream& out, const OCAMCamera::Parameters& params)\n{\n    out << \"Camera Parameters:\" << std::endl;\n    out << \"    model_type \" << \"scaramuzza\" << std::endl;\n    out << \"   camera_name \" << params.m_cameraName << std::endl;\n    out << \"   image_width \" << params.m_imageWidth << std::endl;\n    out << \"  image_height \" << params.m_imageHeight << std::endl;\n\n    out << std::fixed << std::setprecision(10);\n\n    out << \"Poly Parameters\" << std::endl;\n    for(int i=0; i < SCARAMUZZA_POLY_SIZE; i++)\n        out << std::string(\"p\") + boost::lexical_cast<std::string>(i) << \": \" << params.m_poly[i] << std::endl;\n\n    out << \"Inverse Poly Parameters\" << std::endl;\n    for(int i=0; i < SCARAMUZZA_INV_POLY_SIZE; i++)\n        out << std::string(\"p\") + boost::lexical_cast<std::string>(i) << \": \" << params.m_inv_poly[i] << std::endl;\n\n    out << \"Affine Parameters\" << std::endl;\n    out << \"            ac \" << params.m_C << std::endl\n        << \"            ad \" << params.m_D << std::endl\n        << \"            ae \" << params.m_E << std::endl;\n    out << \"            cx \" << params.m_center_x << std::endl\n        << \"            cy \" << params.m_center_y << std::endl;\n\n    return out;\n}\n\nOCAMCamera::OCAMCamera()\n : m_inv_scale(0.0)\n{\n\n}\n\nOCAMCamera::OCAMCamera(const OCAMCamera::Parameters& params)\n : mParameters(params)\n{\n    m_inv_scale = 1.0 / (params.C() - params.D() * params.E());\n}\n\nCamera::ModelType\nOCAMCamera::modelType(void) const\n{\n    return mParameters.modelType();\n}\n\nconst std::string&\nOCAMCamera::cameraName(void) const\n{\n    return mParameters.cameraName();\n}\n\nint\nOCAMCamera::imageWidth(void) const\n{\n    return mParameters.imageWidth();\n}\n\nint\nOCAMCamera::imageHeight(void) const\n{\n    return mParameters.imageHeight();\n}\n\nvoid\nOCAMCamera::estimateIntrinsics(const cv::Size& boardSize,\n                               const std::vector< std::vector<cv::Point3f> >& objectPoints,\n                               const std::vector< std::vector<cv::Point2f> >& imagePoints)\n{\n    // std::cout << \"OCAMCamera::estimateIntrinsics - NOT IMPLEMENTED\" << std::endl;\n    // throw std::string(\"OCAMCamera::estimateIntrinsics - NOT IMPLEMENTED\");\n\n    // Reference: Page 30 of\n    // \" Scaramuzza, D. Omnidirectional Vision: from Calibration to Robot Motion Estimation, ETH Zurich. Thesis no. 17635.\"\n    // http://e-collection.library.ethz.ch/eserv/eth:30301/eth-30301-02.pdf\n    // Matlab code: calibrate.m\n\n    // First, estimate every image's extrinsics parameters\n    std::vector< Eigen::Matrix3d > RList;\n    std::vector< Eigen::Vector3d > TList;\n\n    RList.reserve(imagePoints.size());\n    TList.reserve(imagePoints.size());\n\n    // i-th image\n    for (size_t image_index = 0; image_index < imagePoints.size(); ++image_index)\n    {\n        const std::vector<cv::Point3f>& objPts = objectPoints.at(image_index);\n        const std::vector<cv::Point2f>& imgPts = imagePoints.at(image_index);\n\n        assert(objPts.size() == imgPts.size());\n        assert(objPts.size() == static_cast<unsigned int>(boardSize.width * boardSize.height));\n\n        Eigen::MatrixXd M(objPts.size(), 6);\n\n        for(size_t corner_index = 0; corner_index < objPts.size(); ++corner_index) {\n            double X = objPts.at(corner_index).x;\n            double Y = objPts.at(corner_index).y;\n            assert(objPts.at(corner_index).z==0.0);\n            \n            double u = imgPts.at(corner_index).x;\n            double v = imgPts.at(corner_index).y;\n\n            M(corner_index, 0) = -v * X;\n            M(corner_index, 1) = -v * Y;\n            M(corner_index, 2) =  u * X;\n            M(corner_index, 3) =  u * Y;\n            M(corner_index, 4) = -v;\n            M(corner_index, 5) =  u;\n        }\n\n        Eigen::JacobiSVD<Eigen::MatrixXd> svd(M, Eigen::ComputeFullU | Eigen::ComputeFullV);\n        assert(svd.matrixV().cols() == 6);\n        Eigen::VectorXd h = -svd.matrixV().col(5);\n\n        // scaled version of R and T\n        const double sr11 = h(0);\n        const double sr12 = h(1);\n        const double sr21 = h(2);\n        const double sr22 = h(3);\n        const double st1  = h(4);\n        const double st2  = h(5);\n\n        const double AA = square(sr11*sr12 + sr21*sr22);\n        const double BB = square(sr11) + square(sr21);\n        const double CC = square(sr12) + square(sr22);\n\n        const double sr32_squared_1 = (- (CC-BB) + sqrt(square(CC-BB) + 4.0 * AA)) / 2.0;\n        const double sr32_squared_2 = (- (CC-BB) - sqrt(square(CC-BB) + 4.0 * AA)) / 2.0;\n\n// printf(\"rst = %.12f\\n\", sr32_squared_1*sr32_squared_1 + (CC-BB)*sr32_squared_1 - AA);\n\n        std::vector<double> sr32_squared_values;\n        if (sr32_squared_1 > 0) sr32_squared_values.push_back(sr32_squared_1);\n        if (sr32_squared_2 > 0) sr32_squared_values.push_back(sr32_squared_2);\n        assert(!sr32_squared_values.empty());\n\n        std::vector<double> sr32_values;\n        std::vector<double> sr31_values;\n        for (auto sr32_squared : sr32_squared_values) {\n            for(int sign = -1; sign <= 1; sign += 2) {\n                const double sr32 = static_cast<double>(sign) * std::sqrt(sr32_squared);\n                sr32_values.push_back( sr32 );\n                if (sr32_squared == 0.0) {\n                    // sr31 can be calculated through norm equality, \n                    // but it has positive and negative posibilities\n                    // positive one\n                    sr31_values.push_back(std::sqrt(CC-BB));\n                    // negative one\n                    sr32_values.push_back( sr32 );\n                    sr31_values.push_back(-std::sqrt(CC-BB));\n                    \n                    break; // skip the same situation\n                } else {\n                    // sr31 can be calculated throught dot product == 0\n                    sr31_values.push_back(- (sr11*sr12 + sr21*sr22) / sr32);\n                }\n            }\n        }\n\n        // std::cout << \"h= \" << std::setprecision(12) << h.transpose() << std::endl;\n        // std::cout << \"length: \" << sr32_values.size() << \" & \" << sr31_values.size() << std::endl;\n\n        assert(!sr31_values.empty());\n        assert(sr31_values.size() == sr32_values.size());\n        \n        std::vector<Eigen::Matrix3d> H_values;\n        for(size_t i=0;i<sr31_values.size(); ++i) {\n            const double sr31 = sr31_values.at(i);\n            const double sr32 = sr32_values.at(i);\n            const double lambda = 1.0 / sqrt(sr11*sr11 + sr21*sr21 + sr31*sr31);\n            Eigen::Matrix3d H;\n            H.setZero();\n            H(0,0) = sr11; H(0,1) = sr12; H(0,2) = st1;\n            H(1,0) = sr21; H(1,1) = sr22; H(1,2) = st2;\n            H(2,0) = sr31; H(2,1) = sr32; H(2,2) = 0;\n\n            H_values.push_back( lambda * H);\n            H_values.push_back(-lambda * H);\n        }\n\n        for(auto& H : H_values) {\n            // std::cout << \"H=\\n\" << H << std::endl;\n            Eigen::Matrix3d R;\n            R.col(0) = H.col(0);\n            R.col(1) = H.col(1);\n            R.col(2) = H.col(0).cross(H.col(1));\n            // std::cout << \"R33 = \" << R(2,2) << std::endl;\n        }\n        \n        std::vector<Eigen::Matrix3d> H_candidates;\n\n        for (auto& H : H_values)\n        {\n            Eigen::MatrixXd A_mat(2 * imagePoints.at(image_index).size(), 4);\n            Eigen::VectorXd B_vec(2 * imagePoints.at(image_index).size());\n            A_mat.setZero();\n            B_vec.setZero();\n\n            size_t line_index = 0;\n\n            // iterate images\n            const double& r11 = H(0,0);\n            const double& r12 = H(0,1);\n            // const double& r13 = H(0,2);\n            const double& r21 = H(1,0);\n            const double& r22 = H(1,1);\n            // const double& r23 = H(1,2);\n            const double& r31 = H(2,0);\n            const double& r32 = H(2,1);\n            // const double& r33 = H(2,2);\n            const double& t1  = H(0);\n            const double& t2  = H(1);\n                \n            // iterate chessboard corners in the image\n            for(size_t j=0; j<imagePoints.at(image_index).size(); ++j) {\n                assert(line_index == 2 * j);\n\n                const double& X = objectPoints.at(image_index).at(j).x;\n                const double& Y = objectPoints.at(image_index).at(j).y;\n                const double& u = imagePoints.at(image_index).at(j).x;\n                const double& v = imagePoints.at(image_index).at(j).y;\n\n                double A = r21 * X + r22 * Y + t2;\n                double B = v * (r31 * X + r32 * Y);\n                double C = r11 * X + r12 * Y + t1;\n                double D = u * (r31 * X + r32 * Y);\n                double rou = std::sqrt(u*u + v*v);\n\n                \n                A_mat(line_index+0, 0) = A;\n                A_mat(line_index+1, 0) = C;\n                A_mat(line_index+0, 1) = A * rou;\n                A_mat(line_index+1, 1) = C * rou;\n                A_mat(line_index+0, 2) = A * rou * rou;\n                A_mat(line_index+1, 2) = C * rou * rou;\n                \n                A_mat(line_index+0, 3) = -v;\n                A_mat(line_index+1, 3) = -u;\n                B_vec(line_index+0) = B;\n                B_vec(line_index+1) = D;\n\n                line_index += 2;\n            }\n\n            assert(line_index == static_cast<unsigned int>(A_mat.rows()));\n\n            // pseudo-inverse for polynomial parameters and all t3s\n            {\n                Eigen::JacobiSVD<Eigen::MatrixXd> svd(A_mat, Eigen::ComputeThinU | Eigen::ComputeThinV);\n\n                Eigen::VectorXd x = svd.solve(B_vec);\n\n                // std::cout << \"x(poly and t3) = \" << x << std::endl;\n\n                if (x(2) > 0 && x(3) > 0) {\n                    H_candidates.push_back(H);\n                }\n            }\n        }\n\n        // printf(\"H_candidates.size()=%zu\\n\", H_candidates.size());\n        assert(H_candidates.size()==1);\n\n        Eigen::Matrix3d& H = H_candidates.front();\n\n        Eigen::Matrix3d R; \n        R.col(0) = H.col(0); \n        R.col(1) = H.col(1); \n        R.col(2) = H.col(0).cross(H.col(1)); \n\n        Eigen::Vector3d T = H.col(2);\n        RList.push_back(R);\n        TList.push_back(T);\n\n        // std::cout << \"#\" << image_index << \" frame\" << \" R =\" << R << \" \\nT = \" << T.transpose() << std::endl;\n    }\n\n    // Second, estimate camera intrinsic parameters and all t3\n    Eigen::MatrixXd A_mat(2 * imagePoints.size() * imagePoints.at(0).size(), SCARAMUZZA_POLY_SIZE-1 + imagePoints.size());\n    Eigen::VectorXd B_vec(2 * imagePoints.size() * imagePoints.at(0).size());\n    A_mat.setZero();\n    B_vec.setZero();\n\n    size_t line_index = 0;\n\n    // iterate images\n    for(size_t i = 0; i < imagePoints.size(); ++i) {\n        const double& r11 = RList.at(i)(0,0);\n        const double& r12 = RList.at(i)(0,1);\n        // const double& r13 = RList.at(i)(0,2);\n        const double& r21 = RList.at(i)(1,0);\n        const double& r22 = RList.at(i)(1,1);\n        // const double& r23 = RList.at(i)(1,2);\n        const double& r31 = RList.at(i)(2,0);\n        const double& r32 = RList.at(i)(2,1);\n        // const double& r33 = RList.at(i)(2,2);\n        const double& t1  = TList.at(i)(0);\n        const double& t2  = TList.at(i)(1);\n        \n        // iterate chessboard corners in the image\n        for(size_t j=0; j<imagePoints.at(i).size(); ++j) {\n            assert(line_index == 2 * (i * imagePoints.at(0).size() + j));\n\n            const double& X = objectPoints.at(i).at(j).x;\n            const double& Y = objectPoints.at(i).at(j).y;\n            const double& u = imagePoints.at(i).at(j).x;\n            const double& v = imagePoints.at(i).at(j).y;\n\n            double A = r21 * X + r22 * Y + t2;\n            double B = v * (r31 * X + r32 * Y);\n            double C = r11 * X + r12 * Y + t1;\n            double D = u * (r31 * X + r32 * Y);\n            double rou = std::sqrt(u*u + v*v);\n\n            for(int k=1;k<=SCARAMUZZA_POLY_SIZE-1;++k) {\n                double pow_rou = 0.0;\n                if (k == 1) {\n                    pow_rou = 1.0;\n                }\n                else {\n                    pow_rou = std::pow(rou, k);\n                }\n\n                A_mat(line_index+0, k-1) = A * pow_rou;\n                A_mat(line_index+1, k-1) = C * pow_rou;\n            }\n            \n            A_mat(line_index+0, SCARAMUZZA_POLY_SIZE-1+i) = -v;\n            A_mat(line_index+1, SCARAMUZZA_POLY_SIZE-1+i) = -u;\n            B_vec(line_index+0) = B;\n            B_vec(line_index+1) = D;\n\n            line_index += 2;\n        }\n    }\n\n    assert(line_index == static_cast<unsigned int>(A_mat.rows()));\n\n    Eigen::Matrix<double, SCARAMUZZA_POLY_SIZE, 1> poly_coeff;\n    // pseudo-inverse for polynomial parameters and all t3s\n    {\n        Eigen::JacobiSVD<Eigen::MatrixXd> svd(A_mat, Eigen::ComputeThinU | Eigen::ComputeThinV);\n\n        Eigen::VectorXd x = svd.solve(B_vec);\n\n        poly_coeff[0] = x(0);\n        poly_coeff[1] = 0.0;\n        for(int i=1;i<poly_coeff.size()-1;++i) {\n            poly_coeff[i+1] = x(i);\n        }\n        assert(x.size() == static_cast<unsigned int>(SCARAMUZZA_POLY_SIZE-1+TList.size()));\n    }\n\n    Parameters params = getParameters();\n\n    // Affine matrix A is constructed as [C D; E 1]\n    params.C() = 1.0;\n    params.D() = 0.0;\n    params.E() = 0.0;\n\n    params.center_x() = params.imageWidth() / 2.0;\n    params.center_y() = params.imageHeight() / 2.0;\n    \n    for(size_t i=0; i<SCARAMUZZA_POLY_SIZE; ++i) {\n        params.poly(i) = poly_coeff[i];\n    }\n\n    // params.poly(0) = -216.9657476318;\n    // params.poly(1) = 0.0;\n    // params.poly(2) = 0.0017866911;\n    // params.poly(3) = -0.0000019866;\n    // params.poly(4) =  0.0000000077;\n\n    \n    // inv_poly\n    {\n        std::vector<double> rou_vec;\n        std::vector<double> z_vec;\n        for(double rou = 0.0; rou <= (params.imageWidth() + params.imageHeight())/2; rou += 0.1) {\n            double rou_pow_k = 1.0;\n            double z = 0.0;\n\n            for (int k = 0; k < SCARAMUZZA_POLY_SIZE; k++)\n            {\n                z += rou_pow_k * params.poly(k);\n                rou_pow_k *= rou;\n            }\n\n            rou_vec.push_back(rou);\n            z_vec.push_back(z);\n        }\n\n        assert(rou_vec.size() == z_vec.size());\n        Eigen::VectorXd xVec(rou_vec.size());\n        Eigen::VectorXd yVec(rou_vec.size());\n\n        for(size_t i=0; i<rou_vec.size(); ++i) {\n            xVec(i) = std::atan2(-z_vec.at(i), rou_vec.at(i));\n            yVec(i) = rou_vec.at(i);\n        }\n\n        // use lower order poly to eliminate over-fitting cause by noisy/inaccurate data\n        const int poly_fit_order = 4;\n        Eigen::VectorXd inv_poly_coeff = polyfit(xVec, yVec, poly_fit_order);\n        \n        for(int i=0; i<=poly_fit_order; ++i) {\n            params.inv_poly(i) = inv_poly_coeff(i);\n        }\n    }\n\n    setParameters(params);\n\n    std::cout << \"initial params:\\n\" << params << std::endl; \n}\n\n/** \n * \\brief Lifts a point from the image plane to the unit sphere\n *\n * \\param p image coordinates\n * \\param P coordinates of the point on the sphere\n */\nvoid\nOCAMCamera::liftSphere(const Eigen::Vector2d& p, Eigen::Vector3d& P) const\n{\n    liftProjective(p, P);\n    P.normalize();\n}\n\n/** \n * \\brief Lifts a point from the image plane to its projective ray\n *\n * \\param p image coordinates\n * \\param P coordinates of the projective ray\n */\nvoid\nOCAMCamera::liftProjective(const Eigen::Vector2d& p, Eigen::Vector3d& P) const\n{\n    // Relative to Center\n    Eigen::Vector2d xc(p[0] - mParameters.center_x(), p[1] - mParameters.center_y());\n\n    // Affine Transformation\n    // xc_a = inv(A) * xc;\n    Eigen::Vector2d xc_a(\n        m_inv_scale * (xc[0] - mParameters.D() * xc[1]),\n        m_inv_scale * (-mParameters.E() * xc[0] + mParameters.C() * xc[1])\n    );\n\n    double phi = std::sqrt(xc_a[0] * xc_a[0] + xc_a[1] * xc_a[1]);\n    double phi_i = 1.0;\n    double z = 0.0;\n\n    for (int i = 0; i < SCARAMUZZA_POLY_SIZE; i++)\n    {\n        z += phi_i * mParameters.poly(i);\n        phi_i *= phi;\n    }\n\n    P << xc[0], xc[1], -z;\n}\n\n\n/** \n * \\brief Project a 3D point (\\a x,\\a y,\\a z) to the image plane in (\\a u,\\a v)\n *\n * \\param P 3D point coordinates\n * \\param p return value, contains the image point coordinates\n */\nvoid\nOCAMCamera::spaceToPlane(const Eigen::Vector3d& P, Eigen::Vector2d& p) const\n{\n    double norm = std::sqrt(P[0] * P[0] + P[1] * P[1]);\n    double theta = std::atan2(-P[2], norm);\n    double rho = 0.0;\n    double theta_i = 1.0;\n\n    for (int i = 0; i < SCARAMUZZA_INV_POLY_SIZE; i++)\n    {\n        rho += theta_i * mParameters.inv_poly(i);\n        theta_i *= theta;\n    }\n\n    double invNorm = 1.0 / norm;\n    Eigen::Vector2d xn(\n        P[0] * invNorm * rho,\n        P[1] * invNorm * rho\n    );\n\n    p << xn[0] * mParameters.C() + xn[1] * mParameters.D() + mParameters.center_x(),\n         xn[0] * mParameters.E() + xn[1]                   + mParameters.center_y();\n}\n\n\n/** \n * \\brief Projects an undistorted 2D point p_u to the image plane\n *\n * \\param p_u 2D point coordinates\n * \\return image point coordinates\n */\nvoid\nOCAMCamera::undistToPlane(const Eigen::Vector2d& p_u, Eigen::Vector2d& p) const\n{\n    Eigen::Vector3d P(p_u[0], p_u[1], 1.0);\n    spaceToPlane(P, p);\n}\n\n\n#if 0\nvoid\nOCAMCamera::initUndistortMap(cv::Mat& map1, cv::Mat& map2, double fScale) const\n{\n    cv::Size imageSize(mParameters.imageWidth(), mParameters.imageHeight());\n\n    cv::Mat mapX = cv::Mat::zeros(imageSize, CV_32F);\n    cv::Mat mapY = cv::Mat::zeros(imageSize, CV_32F);\n\n    for (int v = 0; v < imageSize.height; ++v)\n    {\n        for (int u = 0; u < imageSize.width; ++u)\n        {\n            double mx_u = m_inv_K11 / fScale * u + m_inv_K13 / fScale;\n            double my_u = m_inv_K22 / fScale * v + m_inv_K23 / fScale;\n\n            double xi = mParameters.xi();\n            double d2 = mx_u * mx_u + my_u * my_u;\n\n            Eigen::Vector3d P;\n            P << mx_u, my_u, 1.0 - xi * (d2 + 1.0) / (xi + sqrt(1.0 + (1.0 - xi * xi) * d2));\n\n            Eigen::Vector2d p;\n            spaceToPlane(P, p);\n\n            mapX.at<float>(v,u) = p(0);\n            mapY.at<float>(v,u) = p(1);\n        }\n    }\n\n    cv::convertMaps(mapX, mapY, map1, map2, CV_32FC1, false);\n}\n#endif\n\ncv::Mat\nOCAMCamera::initUndistortRectifyMap(cv::Mat& map1, cv::Mat& map2,\n                                    float fx, float fy,\n                                    cv::Size imageSize,\n                                    float cx, float cy,\n                                    cv::Mat rmat) const\n{\n    if (imageSize == cv::Size(0, 0))\n    {\n        imageSize = cv::Size(mParameters.imageWidth(), mParameters.imageHeight());\n    }\n\n    cv::Mat mapX = cv::Mat::zeros(imageSize.height, imageSize.width, CV_32F);\n    cv::Mat mapY = cv::Mat::zeros(imageSize.height, imageSize.width, CV_32F);\n\n    Eigen::Matrix3f K_rect;\n\n    K_rect << fx, 0, cx < 0 ? imageSize.width / 2 : cx,\n              0, fy, cy < 0 ? imageSize.height / 2 : cy,\n              0, 0, 1;\n\n    if (fx < 0 || fy < 0)\n    {\n        throw std::string(std::string(__FUNCTION__) + \": Focal length must be specified\");\n    }\n\n    Eigen::Matrix3f K_rect_inv = K_rect.inverse();\n\n    Eigen::Matrix3f R, R_inv;\n    cv::cv2eigen(rmat, R);\n    R_inv = R.inverse();\n\n    for (int v = 0; v < imageSize.height; ++v)\n    {\n        for (int u = 0; u < imageSize.width; ++u)\n        {\n            Eigen::Vector3f xo;\n            xo << u, v, 1;\n\n            Eigen::Vector3f uo = R_inv * K_rect_inv * xo;\n\n            Eigen::Vector2d p;\n            spaceToPlane(uo.cast<double>(), p);\n\n            mapX.at<float>(v,u) = p(0);\n            mapY.at<float>(v,u) = p(1);\n        }\n    }\n\n    cv::convertMaps(mapX, mapY, map1, map2, CV_32FC1, false);\n\n    cv::Mat K_rect_cv;\n    cv::eigen2cv(K_rect, K_rect_cv);\n    return K_rect_cv;\n}\n\nint\nOCAMCamera::parameterCount(void) const\n{\n    return SCARAMUZZA_CAMERA_NUM_PARAMS;\n}\n\nconst OCAMCamera::Parameters&\nOCAMCamera::getParameters(void) const\n{\n    return mParameters;\n}\n\nvoid\nOCAMCamera::setParameters(const OCAMCamera::Parameters& parameters)\n{\n    mParameters = parameters;\n\n    m_inv_scale = 1.0 / (parameters.C() - parameters.D() * parameters.E());\n}\n\nvoid\nOCAMCamera::readParameters(const std::vector<double>& parameterVec)\n{\n    if ((int)parameterVec.size() != parameterCount())\n    {\n        return;\n    }\n\n    Parameters params = getParameters();\n\n    params.C() = parameterVec.at(0);\n    params.D() = parameterVec.at(1);\n    params.E() = parameterVec.at(2);\n    params.center_x() = parameterVec.at(3);\n    params.center_y() = parameterVec.at(4);\n    for (int i=0; i < SCARAMUZZA_POLY_SIZE; i++)\n        params.poly(i) = parameterVec.at(5+i);\n    for (int i=0; i < SCARAMUZZA_INV_POLY_SIZE; i++)\n        params.inv_poly(i) = parameterVec.at(5 + SCARAMUZZA_POLY_SIZE + i);\n\n    setParameters(params);\n}\n\nvoid\nOCAMCamera::writeParameters(std::vector<double>& parameterVec) const\n{\n    parameterVec.resize(parameterCount());\n    parameterVec.at(0) = mParameters.C();\n    parameterVec.at(1) = mParameters.D();\n    parameterVec.at(2) = mParameters.E();\n    parameterVec.at(3) = mParameters.center_x();\n    parameterVec.at(4) = mParameters.center_y();\n    for (int i=0; i < SCARAMUZZA_POLY_SIZE; i++)\n        parameterVec.at(5+i) = mParameters.poly(i);\n    for (int i=0; i < SCARAMUZZA_INV_POLY_SIZE; i++)\n        parameterVec.at(5 + SCARAMUZZA_POLY_SIZE + i) = mParameters.inv_poly(i);\n}\n\nvoid\nOCAMCamera::writeParametersToYamlFile(const std::string& filename) const\n{\n    mParameters.writeToYamlFile(filename);\n}\n\nstd::string\nOCAMCamera::parametersToString(void) const\n{\n    std::ostringstream oss;\n    oss << mParameters;\n\n    return oss.str();\n}\n\n}\n"
  },
  {
    "path": "src/utils/camodocal/src/chessboard/Chessboard.cc",
    "content": "#include \"camodocal/chessboard/Chessboard.h\"\n\n#include <opencv2/calib3d/calib3d.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n\n#include \"camodocal/chessboard/ChessboardQuad.h\"\n#include \"camodocal/chessboard/Spline.h\"\n\n#define MAX_CONTOUR_APPROX  7\n\nnamespace camodocal\n{\n\nChessboard::Chessboard(cv::Size boardSize, cv::Mat& image)\n : mBoardSize(boardSize)\n , mCornersFound(false)\n{\n    if (image.channels() == 1)\n    {\n        cv::cvtColor(image, mSketch, CV_GRAY2BGR);\n        image.copyTo(mImage);\n    }\n    else\n    {\n        image.copyTo(mSketch);\n        cv::cvtColor(image, mImage, CV_BGR2GRAY);\n    }\n}\n\nvoid\nChessboard::findCorners(bool useOpenCV)\n{\n    mCornersFound = findChessboardCorners(mImage, mBoardSize, mCorners,\n                                          CV_CALIB_CB_ADAPTIVE_THRESH +\n                                          CV_CALIB_CB_NORMALIZE_IMAGE +\n                                          CV_CALIB_CB_FILTER_QUADS +\n                                          CV_CALIB_CB_FAST_CHECK,\n                                          useOpenCV);\n\n    if (mCornersFound)\n    {\n        // draw chessboard corners\n        cv::drawChessboardCorners(mSketch, mBoardSize, mCorners, mCornersFound);\n    }\n}\n\nconst std::vector<cv::Point2f>&\nChessboard::getCorners(void) const\n{\n    return mCorners;\n}\n\nbool\nChessboard::cornersFound(void) const\n{\n    return mCornersFound;\n}\n\nconst cv::Mat&\nChessboard::getImage(void) const\n{\n    return mImage;\n}\n\nconst cv::Mat&\nChessboard::getSketch(void) const\n{\n    return mSketch;\n}\n\nbool\nChessboard::findChessboardCorners(const cv::Mat& image,\n                                  const cv::Size& patternSize,\n                                  std::vector<cv::Point2f>& corners,\n                                  int flags, bool useOpenCV)\n{\n    if (useOpenCV)\n    {\n        return cv::findChessboardCorners(image, patternSize, corners, flags);\n    }\n    else\n    {\n        return findChessboardCornersImproved(image, patternSize, corners, flags);\n    }\n}\n\nbool\nChessboard::findChessboardCornersImproved(const cv::Mat& image,\n                                          const cv::Size& patternSize,\n                                          std::vector<cv::Point2f>& corners,\n                                          int flags)\n{\n    /************************************************************************************\\\n        This is improved variant of chessboard corner detection algorithm that\n        uses a graph of connected quads. It is based on the code contributed\n        by Vladimir Vezhnevets and Philip Gruebele.\n        Here is the copyright notice from the original Vladimir's code:\n        ===============================================================\n\n        The algorithms developed and implemented by Vezhnevets Vldimir\n        aka Dead Moroz (vvp@graphics.cs.msu.ru)\n        See http://graphics.cs.msu.su/en/research/calibration/opencv.html\n        for detailed information.\n\n        Reliability additions and modifications made by Philip Gruebele.\n        <a href=\"mailto:pgruebele@cox.net\">pgruebele@cox.net</a>\n\n        Some improvements were made by:\n        1) Martin Rufli - increased chance of correct corner matching\n           Rufli, M., Scaramuzza, D., and Siegwart, R. (2008),\n           Automatic Detection of Checkerboards on Blurred and Distorted Images,\n           Proceedings of the IEEE/RSJ International Conference on\n           Intelligent Robots and Systems (IROS 2008), Nice, France, September 2008.\n        2) Lionel Heng - post-detection checks and better thresholding\n\n    \\************************************************************************************/\n\n    //int bestDilation        = -1;\n    const int minDilations    =  0;\n    const int maxDilations    =  7;\n\n    std::vector<ChessboardQuadPtr> outputQuadGroup;\n\n    if (image.depth() != CV_8U || image.channels() == 2)\n    {\n        return false;\n    }\n\n    if (patternSize.width < 2 || patternSize.height < 2)\n    {\n        return false;\n    }\n\n    if (patternSize.width > 127 || patternSize.height > 127)\n    {\n        return false;\n    }\n\n    cv::Mat img = image;\n\n    // Image histogram normalization and\n    // BGR to Grayscale image conversion (if applicable)\n    // MARTIN: Set to \"false\"\n    if (image.channels() != 1 || (flags & CV_CALIB_CB_NORMALIZE_IMAGE))\n    {\n        cv::Mat norm_img(image.rows, image.cols, CV_8UC1);\n\n        if (image.channels() != 1)\n        {\n            cv::cvtColor(image, norm_img, CV_BGR2GRAY);\n            img = norm_img;\n        }\n\n        if (flags & CV_CALIB_CB_NORMALIZE_IMAGE)\n        {\n            cv::equalizeHist(image, norm_img);\n            img = norm_img;\n        }\n    }\n\n    if (flags & CV_CALIB_CB_FAST_CHECK)\n    {\n        if (!checkChessboard(img, patternSize))\n        {\n            return false;\n        }\n    }\n\n    // PART 1: FIND LARGEST PATTERN\n    //-----------------------------------------------------------------------\n    // Checker patterns are tried to be found by dilating the background and\n    // then applying a canny edge finder on the closed contours (checkers).\n    // Try one dilation run, but if the pattern is not found, repeat until\n    // max_dilations is reached.\n\n    int prevSqrSize = 0;\n    bool found = false;\n    std::vector<ChessboardCornerPtr> outputCorners;\n\n    for (int k = 0; k < 6; ++k)\n    {\n        for (int dilations = minDilations; dilations <= maxDilations; ++dilations)\n        {\n            if (found)\n            {\n                break;\n            }\n\n            cv::Mat thresh_img;\n\n            // convert the input grayscale image to binary (black-n-white)\n            if (flags & CV_CALIB_CB_ADAPTIVE_THRESH)\n            {\n                int blockSize = lround(prevSqrSize == 0 ?\n                    std::min(img.cols,img.rows)*(k%2 == 0 ? 0.2 : 0.1): prevSqrSize*2)|1;\n\n                // convert to binary\n                cv::adaptiveThreshold(img, thresh_img, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, blockSize, (k/2)*5);\n            }\n            else\n            {\n                // empiric threshold level\n                double mean = (cv::mean(img))[0];\n                int thresh_level = lround(mean - 10);\n                thresh_level = std::max(thresh_level, 10);\n\n                cv::threshold(img, thresh_img, thresh_level, 255, CV_THRESH_BINARY);\n            }\n\n            // MARTIN's Code\n            // Use both a rectangular and a cross kernel. In this way, a more\n            // homogeneous dilation is performed, which is crucial for small,\n            // distorted checkers. Use the CROSS kernel first, since its action\n            // on the image is more subtle\n            cv::Mat kernel1 = cv::getStructuringElement(CV_SHAPE_CROSS, cv::Size(3,3), cv::Point(1,1));\n            cv::Mat kernel2 = cv::getStructuringElement(CV_SHAPE_RECT, cv::Size(3,3), cv::Point(1,1));\n\n            if (dilations >= 1)\n                cv::dilate(thresh_img, thresh_img, kernel1);\n            if (dilations >= 2)\n                cv::dilate(thresh_img, thresh_img, kernel2);\n            if (dilations >= 3)\n                cv::dilate(thresh_img, thresh_img, kernel1);\n            if (dilations >= 4)\n                cv::dilate(thresh_img, thresh_img, kernel2);\n            if (dilations >= 5)\n                cv::dilate(thresh_img, thresh_img, kernel1);\n            if (dilations >= 6)\n                cv::dilate(thresh_img, thresh_img, kernel2);\n\n            // In order to find rectangles that go to the edge, we draw a white\n            // line around the image edge. Otherwise FindContours will miss those\n            // clipped rectangle contours. The border color will be the image mean,\n            // because otherwise we risk screwing up filters like cvSmooth()\n            cv::rectangle(thresh_img, cv::Point(0,0),\n                          cv::Point(thresh_img.cols - 1, thresh_img.rows - 1),\n                          CV_RGB(255,255,255), 3, 8);\n\n            // Generate quadrangles in the following function\n            std::vector<ChessboardQuadPtr> quads;\n\n            generateQuads(quads, thresh_img, flags, dilations, true);\n            if (quads.empty())\n            {\n                continue;\n            }\n\n            // The following function finds and assigns neighbor quads to every\n            // quadrangle in the immediate vicinity fulfilling certain\n            // prerequisites\n            findQuadNeighbors(quads, dilations);\n\n            // The connected quads will be organized in groups. The following loop\n            // increases a \"group_idx\" identifier.\n            // The function \"findConnectedQuads assigns all connected quads\n            // a unique group ID.\n            // If more quadrangles were assigned to a given group (i.e. connected)\n            // than are expected by the input variable \"patternSize\", the\n            // function \"cleanFoundConnectedQuads\" erases the surplus\n            // quadrangles by minimizing the convex hull of the remaining pattern.\n\n            for (int group_idx = 0; ; ++group_idx)\n            {\n                std::vector<ChessboardQuadPtr> quadGroup;\n\n                findConnectedQuads(quads, quadGroup, group_idx, dilations);\n\n                if (quadGroup.empty())\n                {\n                    break;\n                }\n\n                cleanFoundConnectedQuads(quadGroup, patternSize);\n\n                // The following function labels all corners of every quad\n                // with a row and column entry.\n                // \"count\" specifies the number of found quads in \"quad_group\"\n                // with group identifier \"group_idx\"\n                // The last parameter is set to \"true\", because this is the\n                // first function call and some initializations need to be\n                // made.\n                labelQuadGroup(quadGroup, patternSize, true);\n\n                found = checkQuadGroup(quadGroup, outputCorners, patternSize);\n\n                float sumDist = 0;\n                int total = 0;\n\n                for (int i = 0; i < (int)outputCorners.size(); ++i)\n                {\n                    int ni = 0;\n                    float avgi = outputCorners.at(i)->meanDist(ni);\n                    sumDist += avgi * ni;\n                    total += ni;\n                }\n                prevSqrSize = lround(sumDist / std::max(total, 1));\n\n                if (found && !checkBoardMonotony(outputCorners, patternSize))\n                {\n                    found = false;\n                }\n            }\n        }\n    }\n\n    if (!found)\n    {\n        return false;\n    }\n    else\n    {\n        corners.clear();\n        corners.reserve(outputCorners.size());\n        for (size_t i = 0; i < outputCorners.size(); ++i)\n        {\n            corners.push_back(outputCorners.at(i)->pt);\n        }\n\n        cv::cornerSubPix(image, corners, cv::Size(11, 11), cv::Size(-1,-1),\n                         cv::TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));\n\n        return true;\n    }\n}\n\n//===========================================================================\n// ERASE OVERHEAD\n//===========================================================================\n// If we found too many connected quads, remove those which probably do not\n// belong.\nvoid\nChessboard::cleanFoundConnectedQuads(std::vector<ChessboardQuadPtr>& quadGroup,\n                                     cv::Size patternSize)\n{\n    cv::Point2f center(0.0f, 0.0f);\n\n    // Number of quads this pattern should contain\n    int count = ((patternSize.width + 1)*(patternSize.height + 1) + 1)/2;\n\n    // If we have more quadrangles than we should, try to eliminate duplicates\n    // or ones which don't belong to the pattern rectangle. Else go to the end\n    // of the function\n    if ((int)quadGroup.size() <= count)\n    {\n        return;\n    }\n\n    // Create an array of quadrangle centers\n    std::vector<cv::Point2f> centers;\n    centers.resize(quadGroup.size());\n\n    for (size_t i = 0; i < quadGroup.size(); ++i)\n    {\n        cv::Point2f ci(0.0f, 0.0f);\n        ChessboardQuadPtr& q = quadGroup[i];\n\n        for (int j = 0; j < 4; ++j)\n        {\n            ci += q->corners[j]->pt;\n        }\n\n        ci *= 0.25f;\n\n        // Centers(i), is the geometric center of quad(i)\n        // Center, is the center of all found quads\n        centers[i] = ci;\n\n        center += ci;\n    }\n\n    center *= 1.0f / quadGroup.size();\n\n    // If we have more quadrangles than we should, we try to eliminate bad\n    // ones based on minimizing the bounding box. We iteratively remove the\n    // point which reduces the size of the bounding box of the blobs the most\n    // (since we want the rectangle to be as small as possible) remove the\n    // quadrange that causes the biggest reduction in pattern size until we\n    // have the correct number\n    while ((int)quadGroup.size() > count)\n    {\n        double minBoxArea = DBL_MAX;\n        int minBoxAreaIndex = -1;\n\n        // For each point, calculate box area without that point\n        for (size_t skip = 0; skip < quadGroup.size(); ++skip)\n        {\n            // get bounding rectangle\n            cv::Point2f temp = centers[skip];\n            centers[skip] = center;\n\n            std::vector<cv::Point2f> hull;\n            cv::convexHull(centers, hull, true, true);\n            centers[skip] = temp;\n\n            double hull_area = fabs(cv::contourArea(hull));\n\n            // remember smallest box area\n            if (hull_area < minBoxArea)\n            {\n                minBoxArea = hull_area;\n                minBoxAreaIndex = skip;\n            }\n        }\n\n        ChessboardQuadPtr& q0 = quadGroup[minBoxAreaIndex];\n\n        // remove any references to this quad as a neighbor\n        for (size_t i = 0; i < quadGroup.size(); ++i)\n        {\n            ChessboardQuadPtr& q = quadGroup.at(i);\n            for (int j = 0; j < 4; ++j)\n            {\n                if (q->neighbors[j] == q0)\n                {\n                    q->neighbors[j].reset();\n                    q->count--;\n                    for (int k = 0; k < 4; ++k)\n                    {\n                        if (q0->neighbors[k] == q)\n                        {\n                            q0->neighbors[k].reset();\n                            q0->count--;\n                            break;\n                        }\n                    }\n                    break;\n                }\n            }\n        }\n        // remove the quad\n        quadGroup.at(minBoxAreaIndex) = quadGroup.back();\n        centers.at(minBoxAreaIndex) = centers.back();\n        quadGroup.pop_back();\n        centers.pop_back();\n    }\n}\n\n//===========================================================================\n// FIND COONECTED QUADS\n//===========================================================================\nvoid\nChessboard::findConnectedQuads(std::vector<ChessboardQuadPtr>& quads,\n                               std::vector<ChessboardQuadPtr>& group,\n                               int group_idx, int dilation)\n{\n    ChessboardQuadPtr q;\n\n    // Scan the array for a first unlabeled quad\n    for (size_t i = 0; i < quads.size(); ++i)\n    {\n        ChessboardQuadPtr& quad = quads.at(i);\n\n        if (quad->count > 0 && quad->group_idx < 0)\n        {\n            q = quad;\n            break;\n        }\n    }\n\n    if (q.get() == 0)\n    {\n        return;\n    }\n\n    // Recursively find a group of connected quads starting from the seed quad\n\n    std::vector<ChessboardQuadPtr> stack;\n    stack.push_back(q);\n\n    group.push_back(q);\n    q->group_idx = group_idx;\n\n    while (!stack.empty())\n    {\n        q = stack.back();\n        stack.pop_back();\n\n        for (int i = 0; i < 4; ++i)\n        {\n            ChessboardQuadPtr& neighbor = q->neighbors[i];\n\n            // If he neighbor exists and the neighbor has more than 0\n            // neighbors and the neighbor has not been classified yet.\n            if (neighbor.get() && neighbor->count > 0 && neighbor->group_idx < 0)\n            {\n                stack.push_back(neighbor);\n                group.push_back(neighbor);\n                neighbor->group_idx = group_idx;\n            }\n        }\n    }\n}\n\nvoid\nChessboard::labelQuadGroup(std::vector<ChessboardQuadPtr>& quadGroup,\n                           cv::Size patternSize, bool firstRun)\n{\n    // If this is the first function call, a seed quad needs to be selected\n    if (firstRun)\n    {\n        // Search for the (first) quad with the maximum number of neighbors\n        // (usually 4). This will be our starting point.\n        int mark = -1;\n        int maxNeighborCount = 0;\n        for (size_t i = 0; i < quadGroup.size(); ++i)\n        {\n            ChessboardQuadPtr& q = quadGroup.at(i);\n            if (q->count > maxNeighborCount)\n            {\n                mark = i;\n                maxNeighborCount = q->count;\n\n                if (maxNeighborCount == 4)\n                {\n                    break;\n                }\n            }\n        }\n\n        // Mark the starting quad's (per definition) upper left corner with\n        //(0,0) and then proceed clockwise\n        // The following labeling sequence enshures a \"right coordinate system\"\n        ChessboardQuadPtr& q = quadGroup.at(mark);\n\n        q->labeled = true;\n\n        q->corners[0]->row = 0;\n        q->corners[0]->column = 0;\n        q->corners[1]->row = 0;\n        q->corners[1]->column = 1;\n        q->corners[2]->row = 1;\n        q->corners[2]->column = 1;\n        q->corners[3]->row = 1;\n        q->corners[3]->column = 0;\n    }\n\n\n    // Mark all other corners with their respective row and column\n    bool flagChanged = true;\n    while (flagChanged)\n    {\n        // First reset the flag to \"false\"\n        flagChanged = false;\n\n        // Go through all quads top down is faster, since unlabeled quads will\n        // be inserted at the end of the list\n        for (int i = quadGroup.size() - 1; i >= 0; --i)\n        {\n            ChessboardQuadPtr& quad = quadGroup.at(i);\n\n            // Check whether quad \"i\" has been labeled already\n             if (!quad->labeled)\n            {\n                // Check its neighbors, whether some of them have been labeled\n                // already\n                for (int j = 0; j < 4; j++)\n                {\n                    // Check whether the neighbor exists (i.e. is not the NULL\n                    // pointer)\n                    if (quad->neighbors[j])\n                    {\n                        ChessboardQuadPtr& quadNeighbor = quad->neighbors[j];\n\n                        // Only proceed, if neighbor \"j\" was labeled\n                        if (quadNeighbor->labeled)\n                        {\n                            // For every quad it could happen to pass here\n                            // multiple times. We therefore \"break\" later.\n                            // Check whitch of the neighbors corners is\n                            // connected to the current quad\n                            int connectedNeighborCornerId = -1;\n                            for (int k = 0; k < 4; ++k)\n                            {\n                                if (quadNeighbor->neighbors[k] == quad)\n                                {\n                                    connectedNeighborCornerId = k;\n\n                                    // there is only one, therefore\n                                    break;\n                                }\n                            }\n\n\n                            // For the following calculations we need the row\n                            // and column of the connected neighbor corner and\n                            // all other corners of the connected quad \"j\",\n                            // clockwise (CW)\n                            ChessboardCornerPtr& conCorner        = quadNeighbor->corners[connectedNeighborCornerId];\n                            ChessboardCornerPtr& conCornerCW1     = quadNeighbor->corners[(connectedNeighborCornerId+1)%4];\n                            ChessboardCornerPtr& conCornerCW2     = quadNeighbor->corners[(connectedNeighborCornerId+2)%4];\n                            ChessboardCornerPtr& conCornerCW3     = quadNeighbor->corners[(connectedNeighborCornerId+3)%4];\n\n                            quad->corners[j]->row            =    conCorner->row;\n                            quad->corners[j]->column        =    conCorner->column;\n                            quad->corners[(j+1)%4]->row        =    conCorner->row - conCornerCW2->row + conCornerCW3->row;\n                            quad->corners[(j+1)%4]->column    =    conCorner->column - conCornerCW2->column + conCornerCW3->column;\n                            quad->corners[(j+2)%4]->row        =    conCorner->row + conCorner->row - conCornerCW2->row;\n                            quad->corners[(j+2)%4]->column    =    conCorner->column + conCorner->column - conCornerCW2->column;\n                            quad->corners[(j+3)%4]->row        =    conCorner->row - conCornerCW2->row + conCornerCW1->row;\n                            quad->corners[(j+3)%4]->column    =    conCorner->column - conCornerCW2->column + conCornerCW1->column;\n\n                            // Mark this quad as labeled\n                            quad->labeled = true;\n\n                            // Changes have taken place, set the flag\n                            flagChanged = true;\n\n                            // once is enough!\n                            break;\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n\n    // All corners are marked with row and column\n    // Record the minimal and maximal row and column indices\n    // It is unlikely that more than 8bit checkers are used per dimension, if there are\n    // an error would have been thrown at the beginning of \"cvFindChessboardCorners2\"\n    int min_row        =  127;\n    int max_row        = -127;\n    int min_column    =  127;\n    int max_column    = -127;\n\n    for (int i = 0; i < (int)quadGroup.size(); ++i)\n    {\n        ChessboardQuadPtr& q = quadGroup.at(i);\n\n        for (int j = 0; j < 4; ++j)\n        {\n            ChessboardCornerPtr& c = q->corners[j];\n\n            if (c->row > max_row)\n            {\n                max_row = c->row;\n            }\n            if (c->row < min_row)\n            {\n                min_row = c->row;\n            }\n            if (c->column > max_column)\n            {\n                max_column = c->column;\n            }\n            if (c->column < min_column)\n            {\n                min_column = c->column;\n            }\n        }\n    }\n\n    // Label all internal corners with \"needsNeighbor\" = false\n    // Label all external corners with \"needsNeighbor\" = true,\n    // except if in a given dimension the pattern size is reached\n    for (int i = min_row; i <= max_row; ++i)\n    {\n        for (int j = min_column; j <= max_column; ++j)\n        {\n            // A flag that indicates, whether a row/column combination is\n            // executed multiple times\n            bool flag = false;\n\n            // Remember corner and quad\n            int cornerID;\n            int quadID;\n\n            for (int k = 0; k < (int)quadGroup.size(); ++k)\n            {\n                ChessboardQuadPtr& q = quadGroup.at(k);\n\n                for (int l = 0; l < 4; ++l)\n                {\n                    if ((q->corners[l]->row == i) && (q->corners[l]->column == j))\n                    {\n                        if (flag)\n                        {\n                            // Passed at least twice through here\n                            q->corners[l]->needsNeighbor = false;\n                            quadGroup[quadID]->corners[cornerID]->needsNeighbor = false;\n                        }\n                        else\n                        {\n                            // Mark with needs a neighbor, but note the\n                            // address\n                            q->corners[l]->needsNeighbor = true;\n                            cornerID = l;\n                            quadID = k;\n                        }\n\n                        // set the flag to true\n                        flag = true;\n                    }\n                }\n            }\n        }\n    }\n\n    // Complete Linking:\n    // sometimes not all corners were properly linked in \"findQuadNeighbors\",\n    // but after labeling each corner with its respective row and column, it is\n    // possible to match them anyway.\n    for (int i = min_row; i <= max_row; ++i)\n    {\n        for (int j = min_column; j <= max_column; ++j)\n        {\n            // the following \"number\" indicates the number of corners which\n            // correspond to the given (i,j) value\n            // 1    is a border corner or a conrer which still needs a neighbor\n            // 2    is a fully connected internal corner\n            // >2    something went wrong during labeling, report a warning\n            int number = 1;\n\n            // remember corner and quad\n            int cornerID;\n            int quadID;\n\n            for (int k = 0; k < (int)quadGroup.size(); ++k)\n            {\n                ChessboardQuadPtr& q = quadGroup.at(k);\n\n                for (int l = 0; l < 4; ++l)\n                {\n                    if ((q->corners[l]->row == i) && (q->corners[l]->column == j))\n                    {\n\n                        if (number == 1)\n                        {\n                            // First corner, note its ID\n                            cornerID = l;\n                            quadID = k;\n                        }\n\n                        else if (number == 2)\n                        {\n                            // Second corner, check wheter this and the\n                            // first one have equal coordinates, else\n                            // interpolate\n                            cv::Point2f delta = q->corners[l]->pt - quadGroup[quadID]->corners[cornerID]->pt;\n\n                            if (delta.x != 0.0f || delta.y != 0.0f)\n                            {\n                                // Interpolate\n                                q->corners[l]->pt -= delta * 0.5f;\n\n                                quadGroup[quadID]->corners[cornerID]->pt += delta * 0.5f;\n                            }\n                        }\n                        else if (number > 2)\n                        {\n                            // Something went wrong during row/column labeling\n                            // Report a Warning\n                            // ->Implemented in the function \"mrWriteCorners\"\n                        }\n\n                        // increase the number by one\n                        ++number;\n                    }\n                }\n            }\n        }\n    }\n\n\n    // Bordercorners don't need any neighbors, if the pattern size in the\n    // respective direction is reached\n    // The only time we can make shure that the target pattern size is reached in a given\n    // dimension, is when the larger side has reached the target size in the maximal\n    // direction, or if the larger side is larger than the smaller target size and the\n    // smaller side equals the smaller target size\n    int largerDimPattern = std::max(patternSize.height,patternSize.width);\n    int smallerDimPattern = std::min(patternSize.height,patternSize.width);\n    bool flagSmallerDim1 = false;\n    bool flagSmallerDim2 = false;\n\n    if ((largerDimPattern + 1) == max_column - min_column)\n    {\n        flagSmallerDim1 = true;\n        // We found out that in the column direction the target pattern size is reached\n        // Therefore border column corners do not need a neighbor anymore\n        // Go through all corners\n        for (int k = 0; k < (int)quadGroup.size(); ++k)\n        {\n            ChessboardQuadPtr& q = quadGroup.at(k);\n\n            for (int l = 0; l < 4; ++l)\n            {\n                ChessboardCornerPtr& c = q->corners[l];\n\n                if (c->column == min_column || c->column == max_column)\n                {\n                    // Needs no neighbor anymore\n                    c->needsNeighbor = false;\n                }\n            }\n        }\n    }\n\n    if ((largerDimPattern + 1) == max_row - min_row)\n    {\n        flagSmallerDim2 = true;\n        // We found out that in the column direction the target pattern size is reached\n        // Therefore border column corners do not need a neighbor anymore\n        // Go through all corners\n        for (int k = 0; k < (int)quadGroup.size(); ++k)\n        {\n            ChessboardQuadPtr& q = quadGroup.at(k);\n\n            for (int l = 0; l < 4; ++l)\n            {\n                ChessboardCornerPtr& c = q->corners[l];\n\n                if (c->row == min_row || c->row == max_row)\n                {\n                    // Needs no neighbor anymore\n                    c->needsNeighbor = false;\n                }\n            }\n        }\n    }\n\n\n    // Check the two flags:\n    //    -    If one is true and the other false, then the pattern target\n    //        size was reached in in one direction -> We can check, whether the target\n    //        pattern size is also reached in the other direction\n    //  -    If both are set to true, then we deal with a square board -> do nothing\n    //  -    If both are set to false -> There is a possibility that the larger side is\n    //        larger than the smaller target size -> Check and if true, then check whether\n    //        the other side has the same size as the smaller target size\n    if ((flagSmallerDim1 == false && flagSmallerDim2 == true))\n    {\n        // Larger target pattern size is in row direction, check wheter smaller target\n        // pattern size is reached in column direction\n        if ((smallerDimPattern + 1) == max_column - min_column)\n        {\n            for (int k = 0; k < (int)quadGroup.size(); ++k)\n            {\n                ChessboardQuadPtr& q = quadGroup.at(k);\n\n                for (int l = 0; l < 4; ++l)\n                {\n                    ChessboardCornerPtr& c = q->corners[l];\n\n                    if (c->column == min_column || c->column == max_column)\n                    {\n                        // Needs no neighbor anymore\n                        c->needsNeighbor = false;\n                    }\n                }\n            }\n        }\n    }\n\n    if ((flagSmallerDim1 == true && flagSmallerDim2 == false))\n    {\n        // Larger target pattern size is in column direction, check wheter smaller target\n        // pattern size is reached in row direction\n        if ((smallerDimPattern + 1) == max_row - min_row)\n        {\n            for (int k = 0; k < (int)quadGroup.size(); ++k)\n            {\n                ChessboardQuadPtr& q = quadGroup.at(k);\n\n                for (int l = 0; l < 4; ++l)\n                {\n                    ChessboardCornerPtr& c = q->corners[l];\n\n                    if (c->row == min_row || c->row == max_row)\n                    {\n                        // Needs no neighbor anymore\n                        c->needsNeighbor = false;\n                    }\n                }\n            }\n        }\n    }\n\n    if ((flagSmallerDim1 == false && flagSmallerDim2 == false) && smallerDimPattern + 1 < max_column - min_column)\n    {\n        // Larger target pattern size is in column direction, check wheter smaller target\n        // pattern size is reached in row direction\n        if ((smallerDimPattern + 1) == max_row - min_row)\n        {\n            for (int k = 0; k < (int)quadGroup.size(); ++k)\n            {\n                ChessboardQuadPtr& q = quadGroup.at(k);\n\n                for (int l = 0; l < 4; ++l)\n                {\n                    ChessboardCornerPtr& c = q->corners[l];\n\n                    if (c->row == min_row || c->row == max_row)\n                    {\n                        // Needs no neighbor anymore\n                        c->needsNeighbor = false;\n                    }\n                }\n            }\n        }\n    }\n\n    if ((flagSmallerDim1 == false && flagSmallerDim2 == false) && smallerDimPattern + 1 < max_row - min_row)\n    {\n        // Larger target pattern size is in row direction, check wheter smaller target\n        // pattern size is reached in column direction\n        if ((smallerDimPattern + 1) == max_column - min_column)\n        {\n            for (int k = 0; k < (int)quadGroup.size(); ++k)\n            {\n                ChessboardQuadPtr& q = quadGroup.at(k);\n\n                for (int l = 0; l < 4; ++l)\n                {\n                    ChessboardCornerPtr& c = q->corners[l];\n\n                    if (c->column == min_column || c->column == max_column)\n                    {\n                        // Needs no neighbor anymore\n                        c->needsNeighbor = false;\n                    }\n                }\n            }\n        }\n    }\n}\n\n//===========================================================================\n// GIVE A GROUP IDX\n//===========================================================================\nvoid\nChessboard::findQuadNeighbors(std::vector<ChessboardQuadPtr>& quads, int dilation)\n{\n    // Thresh dilation is used to counter the effect of dilation on the\n    // distance between 2 neighboring corners. Since the distance below is\n    // computed as its square, we do here the same. Additionally, we take the\n    // conservative assumption that dilation was performed using the 3x3 CROSS\n    // kernel, which coresponds to the 4-neighborhood.\n    const float thresh_dilation = (float)(2*dilation+3)*(2*dilation+3)*2;    // the \"*2\" is for the x and y component\n                                                                            // the \"3\" is for initial corner mismatch\n\n    // Find quad neighbors\n    for (size_t idx = 0; idx < quads.size(); ++idx)\n    {\n        ChessboardQuadPtr& curQuad = quads.at(idx);\n\n        // Go through all quadrangles and label them in groups\n        // For each corner of this quadrangle\n        for (int i = 0; i < 4; ++i)\n        {\n            float minDist = FLT_MAX;\n            int closestCornerIdx = -1;\n            ChessboardQuadPtr closestQuad;\n\n            if (curQuad->neighbors[i])\n            {\n                continue;\n            }\n\n            cv::Point2f pt = curQuad->corners[i]->pt;\n\n            // Find the closest corner in all other quadrangles\n            for (size_t k = 0; k < quads.size(); ++k)\n            {\n                if (k == idx)\n                {\n                    continue;\n                }\n\n                ChessboardQuadPtr& quad = quads.at(k);\n\n                for (int j = 0; j < 4; ++j)\n                {\n                    // If it already has a neighbor\n                    if (quad->neighbors[j])\n                    {\n                        continue;\n                    }\n\n                    cv::Point2f dp = pt - quad->corners[j]->pt;\n                    float dist = dp.dot(dp);\n\n                    // The following \"if\" checks, whether \"dist\" is the\n                    // shortest so far and smaller than the smallest\n                    // edge length of the current and target quads\n                    if (dist < minDist &&\n                        dist <= (curQuad->edge_len + thresh_dilation) &&\n                        dist <= (quad->edge_len + thresh_dilation)   )\n                    {\n                        // Check whether conditions are fulfilled\n                        if (matchCorners(curQuad, i, quad, j))\n                        {\n                            closestCornerIdx = j;\n                            closestQuad = quad;\n                            minDist = dist;\n                        }\n                    }\n                }\n            }\n\n            // Have we found a matching corner point?\n            if (closestCornerIdx >= 0 && minDist < FLT_MAX)\n            {\n                ChessboardCornerPtr closestCorner = closestQuad->corners[closestCornerIdx];\n\n                // Make sure that the closest quad does not have the current\n                // quad as neighbor already\n                bool valid = true;\n                for (int j = 0; j < 4; ++j)\n                {\n                    if (closestQuad->neighbors[j] == curQuad)\n                    {\n                        valid = false;\n                        break;\n                    }\n                }\n                if (!valid)\n                {\n                    continue;\n                }\n\n                // We've found one more corner - remember it\n                closestCorner->pt = (pt + closestCorner->pt) * 0.5f;\n\n                curQuad->count++;\n                curQuad->neighbors[i] = closestQuad;\n                curQuad->corners[i] = closestCorner;\n\n                closestQuad->count++;\n                closestQuad->neighbors[closestCornerIdx] = curQuad;\n                closestQuad->corners[closestCornerIdx] = closestCorner;\n            }\n        }\n    }\n}\n\n\n\n//===========================================================================\n// AUGMENT PATTERN WITH ADDITIONAL QUADS\n//===========================================================================\n// The first part of the function is basically a copy of\n// \"findQuadNeighbors\"\n// The comparisons between two points and two lines could be computed in their\n// own function\nint\nChessboard::augmentBestRun(std::vector<ChessboardQuadPtr>& candidateQuads, int candidateDilation,\n                           std::vector<ChessboardQuadPtr>& existingQuads, int existingDilation)\n{\n    // thresh dilation is used to counter the effect of dilation on the\n    // distance between 2 neighboring corners. Since the distance below is\n    // computed as its square, we do here the same. Additionally, we take the\n    // conservative assumption that dilation was performed using the 3x3 CROSS\n    // kernel, which coresponds to the 4-neighborhood.\n    const float thresh_dilation = (2*candidateDilation+3)*(2*existingDilation+3)*2;    // the \"*2\" is for the x and y component\n\n    // Search all old quads which have a neighbor that needs to be linked\n    for (size_t idx = 0; idx < existingQuads.size(); ++idx)\n    {\n        ChessboardQuadPtr& curQuad = existingQuads.at(idx);\n\n        // For each corner of this quadrangle\n        for (int i = 0; i < 4; ++i)\n        {\n            float minDist = FLT_MAX;\n            int closestCornerIdx = -1;\n            ChessboardQuadPtr closestQuad;\n\n            // If curQuad corner[i] is already linked, continue\n            if (!curQuad->corners[i]->needsNeighbor)\n            {\n                continue;\n            }\n\n            cv::Point2f pt = curQuad->corners[i]->pt;\n\n            // Look for a match in all candidateQuads' corners\n            for (size_t k = 0; k < candidateQuads.size(); ++k)\n            {\n                ChessboardQuadPtr& candidateQuad = candidateQuads.at(k);\n\n                // Only look at unlabeled new quads\n                if (candidateQuad->labeled)\n                {\n                    continue;\n                }\n\n                for (int j = 0; j < 4; ++j)\n                {\n                    // Only proceed if they are less than dist away from each\n                    // other\n                    cv::Point2f dp = pt - candidateQuad->corners[j]->pt;\n                    float dist = dp.dot(dp);\n\n                    if ((dist < minDist) &&\n                        dist <= (curQuad->edge_len + thresh_dilation) &&\n                        dist <= (candidateQuad->edge_len + thresh_dilation))\n                    {\n                        if (matchCorners(curQuad, i, candidateQuad, j))\n                        {\n                            closestCornerIdx = j;\n                            closestQuad = candidateQuad;\n                            minDist = dist;\n                        }\n                    }\n                }\n            }\n\n            // Have we found a matching corner point?\n            if (closestCornerIdx >= 0 && minDist < FLT_MAX)\n            {\n                ChessboardCornerPtr closestCorner = closestQuad->corners[closestCornerIdx];\n                closestCorner->pt = (pt + closestCorner->pt) * 0.5f;\n\n                // We've found one more corner - remember it\n                // ATTENTION: write the corner x and y coordinates separately,\n                // else the crucial row/column entries will be overwritten !!!\n                curQuad->corners[i]->pt = closestCorner->pt;\n                curQuad->neighbors[i] = closestQuad;\n                closestQuad->corners[closestCornerIdx]->pt = closestCorner->pt;\n\n                // Label closest quad as labeled. In this way we exclude it\n                // being considered again during the next loop iteration\n                closestQuad->labeled = true;\n\n                // We have a new member of the final pattern, copy it over\n                ChessboardQuadPtr newQuad(new ChessboardQuad);\n                newQuad->count        = 1;\n                newQuad->edge_len    = closestQuad->edge_len;\n                newQuad->group_idx    = curQuad->group_idx;    //the same as the current quad\n                newQuad->labeled    = false;                //do it right afterwards\n\n                curQuad->neighbors[i] = newQuad;\n\n                // We only know one neighbor for sure\n                newQuad->neighbors[closestCornerIdx] = curQuad;\n\n                for (int j = 0; j < 4; j++)\n                {\n                    newQuad->corners[j].reset(new ChessboardCorner);\n                    newQuad->corners[j]->pt = closestQuad->corners[j]->pt;\n                }\n\n                existingQuads.push_back(newQuad);\n\n                // Start the function again\n                return -1;\n            }\n        }\n    }\n\n    // Finished, don't start the function again\n    return 1;\n}\n\n\n\n//===========================================================================\n// GENERATE QUADRANGLES\n//===========================================================================\nvoid\nChessboard::generateQuads(std::vector<ChessboardQuadPtr>& quads,\n                          cv::Mat& image, int flags,\n                          int dilation, bool firstRun)\n{\n    // Empirical lower bound for the size of allowable quadrangles.\n    // MARTIN, modified: Added \"*0.1\" in order to find smaller quads.\n    int minSize = lround(image.cols * image.rows * .03 * 0.01 * 0.92 * 0.1);\n\n    std::vector< std::vector<cv::Point> > contours;\n    std::vector<cv::Vec4i> hierarchy;\n\n    // Initialize contour retrieving routine\n    cv::findContours(image, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);\n\n    std::vector< std::vector<cv::Point> > quadContours;\n\n    for (size_t i = 0; i < contours.size(); ++i)\n    {\n        // Reject contours with a too small perimeter and contours which are\n        // completely surrounded by another contour\n        // MARTIN: If this function is called during PART 1, then the parameter \"first run\"\n        // is set to \"true\". This guarantees, that only \"nice\" squares are detected.\n        // During PART 2, we allow the polygonal approximation function below to\n        // approximate more freely, which can result in recognized \"squares\" that are in\n        // reality multiple blurred and sticked together squares.\n        std::vector<cv::Point>& contour = contours.at(i);\n\n        if (hierarchy[i][3] == -1 || cv::contourArea(contour) < minSize)\n        {\n            continue;\n        }\n\n        int min_approx_level = 1, max_approx_level;\n        if (firstRun)\n        {\n            max_approx_level = 3;\n        }\n        else\n        {\n            max_approx_level = MAX_CONTOUR_APPROX;\n        }\n\n        std::vector<cv::Point> approxContour;\n        for (int approx_level = min_approx_level; approx_level <= max_approx_level; approx_level++)\n        {\n            cv::approxPolyDP(contour, approxContour, approx_level, true);\n\n            if (approxContour.size() == 4)\n            {\n                break;\n            }\n        }\n\n        // Reject non-quadrangles\n        if (approxContour.size() == 4 && cv::isContourConvex(approxContour))\n        {\n            double p = cv::arcLength(approxContour, true);\n            double area = cv::contourArea(approxContour);\n\n            cv::Point pt[4];\n            for (int i = 0; i < 4; i++)\n            {\n                pt[i] = approxContour[i];\n            }\n\n            cv::Point dp = pt[0] - pt[2];\n            double d1 = sqrt(dp.dot(dp));\n\n            dp = pt[1] - pt[3];\n            double d2 = sqrt(dp.dot(dp));\n\n            // PHILIPG: Only accept those quadrangles which are more\n            // square than rectangular and which are big enough\n            dp = pt[0] - pt[1];\n            double d3 = sqrt(dp.dot(dp));\n            dp = pt[1] - pt[2];\n            double d4 = sqrt(dp.dot(dp));\n\n            if (!(flags & CV_CALIB_CB_FILTER_QUADS) ||\n                (d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > minSize &&\n                d1 >= 0.15 * p && d2 >= 0.15 * p))\n            {\n                quadContours.push_back(approxContour);\n            }\n        }\n    }\n\n    // Allocate quad & corner buffers\n    quads.resize(quadContours.size());\n\n    // Create array of quads structures\n    for (size_t idx = 0; idx < quadContours.size(); ++idx)\n    {\n        ChessboardQuadPtr& q = quads.at(idx);\n        std::vector<cv::Point>& contour = quadContours.at(idx);\n\n        // Reset group ID\n        q.reset(new ChessboardQuad);\n        assert(contour.size() == 4);\n\n        for (int i = 0; i < 4; ++i)\n        {\n            cv::Point2f pt = contour.at(i);\n            ChessboardCornerPtr corner(new ChessboardCorner);\n            corner->pt = pt;\n            q->corners[i] = corner;\n        }\n\n        for (int i = 0; i < 4; ++i)\n        {\n            cv::Point2f dp = q->corners[i]->pt - q->corners[(i+1)&3]->pt;\n            float d = dp.dot(dp);\n            if (q->edge_len > d)\n            {\n                q->edge_len = d;\n            }\n        }\n    }\n}\n\nbool\nChessboard::checkQuadGroup(std::vector<ChessboardQuadPtr>& quads,\n                           std::vector<ChessboardCornerPtr>& corners,\n                           cv::Size patternSize)\n{\n    // Initialize\n    bool flagRow = false;\n    bool flagColumn = false;\n    int height = -1;\n    int width = -1;\n\n    // Compute the minimum and maximum row / column ID\n    // (it is unlikely that more than 8bit checkers are used per dimension)\n    int min_row    =  127;\n    int max_row    = -127;\n    int min_col    =  127;\n    int max_col    = -127;\n\n    for (size_t i = 0; i < quads.size(); ++i)\n    {\n        ChessboardQuadPtr& q = quads.at(i);\n\n        for (int j = 0; j < 4; ++j)\n        {\n            ChessboardCornerPtr& c = q->corners[j];\n\n            if (c->row > max_row)\n            {\n                max_row = c->row;\n            }\n            if (c->row < min_row)\n            {\n                min_row = c->row;\n            }\n            if (c->column > max_col)\n            {\n                max_col = c->column;\n            }\n            if (c->column < min_col)\n            {\n                min_col = c->column;\n            }\n        }\n    }\n\n    // If in a given direction the target pattern size is reached, we know exactly how\n    // the checkerboard is oriented.\n    // Else we need to prepare enough \"dummy\" corners for the worst case.\n    for (size_t i = 0; i < quads.size(); ++i)\n    {\n        ChessboardQuadPtr& q = quads.at(i);\n\n        for (int j = 0; j < 4; ++j)\n        {\n            ChessboardCornerPtr& c = q->corners[j];\n\n            if (c->column == max_col && c->row != min_row && c->row != max_row && !c->needsNeighbor)\n            {\n                flagColumn = true;\n            }\n            if (c->row == max_row && c->column != min_col && c->column != max_col && !c->needsNeighbor)\n            {\n                flagRow = true;\n            }\n        }\n    }\n\n    if (flagColumn)\n    {\n        if (max_col - min_col == patternSize.width + 1)\n        {\n            width = patternSize.width;\n            height = patternSize.height;\n        }\n        else\n        {\n            width = patternSize.height;\n            height = patternSize.width;\n        }\n    }\n    else if (flagRow)\n    {\n        if (max_row - min_row == patternSize.width + 1)\n        {\n            height = patternSize.width;\n            width = patternSize.height;\n        }\n        else\n        {\n            height = patternSize.height;\n            width = patternSize.width;\n        }\n    }\n    else\n    {\n        // If target pattern size is not reached in at least one of the two\n        // directions,  then we do not know where the remaining corners are\n        // located. Account for this.\n        width = std::max(patternSize.width, patternSize.height);\n        height = std::max(patternSize.width, patternSize.height);\n    }\n\n    ++min_row;\n    ++min_col;\n    max_row = min_row + height - 1;\n    max_col = min_col + width - 1;\n\n    corners.clear();\n\n    int linkedBorderCorners = 0;\n\n    // Write the corners in increasing order to the output file\n    for (int i = min_row; i <= max_row; ++i)\n    {\n        for (int j = min_col; j <= max_col; ++j)\n        {\n            // Reset the iterator\n            int iter = 1;\n\n            for (int k = 0; k < (int)quads.size(); ++k)\n            {\n                ChessboardQuadPtr& quad = quads.at(k);\n\n                for (int l = 0; l < 4; ++l)\n                {\n                    ChessboardCornerPtr& c = quad->corners[l];\n\n                    if (c->row == i && c->column == j)\n                    {\n                        bool boardEdge = false;\n                        if (i == min_row || i == max_row ||\n                            j == min_col || j == max_col)\n                        {\n                            boardEdge = true;\n                        }\n\n                        if ((iter == 1 && boardEdge) || (iter == 2 && !boardEdge))\n                        {\n                            // The respective row and column have been found\n                            corners.push_back(quad->corners[l]);\n                        }\n\n                        if (iter == 2 && boardEdge)\n                        {\n                            ++linkedBorderCorners;\n                        }\n\n                        // If the iterator is larger than two, this means that more than\n                        // two corners have the same row / column entries. Then some\n                        // linking errors must have occured and we should not use the found\n                        // pattern\n                        if (iter > 2)\n                        {\n                            return false;\n                        }\n\n                        iter++;\n                    }\n                }\n            }\n        }\n    }\n\n    if ((int)corners.size() != patternSize.width * patternSize.height ||\n        linkedBorderCorners < (patternSize.width * 2 + patternSize.height * 2 - 2) * 0.75f)\n    {\n        return false;\n    }\n\n    // check that no corners lie at image boundary\n    float border = 5.0f;\n    for (int i = 0; i < (int)corners.size(); ++i)\n    {\n        ChessboardCornerPtr& c = corners.at(i);\n\n        if (c->pt.x < border || c->pt.x > mImage.cols - border ||\n            c->pt.y < border || c->pt.y > mImage.rows - border)\n        {\n            return false;\n        }\n    }\n\n    // check if we need to transpose the board\n    if (width != patternSize.width)\n    {\n        std::swap(width, height);\n\n        std::vector<ChessboardCornerPtr> outputCorners;\n        outputCorners.resize(corners.size());\n\n        for (int i = 0; i < height; ++i)\n        {\n            for (int j = 0; j < width; ++j)\n            {\n                outputCorners.at(i * width + j) = corners.at(j * height + i);\n            }\n        }\n\n        corners = outputCorners;\n    }\n\n    // check if we need to revert the order in each row\n    cv::Point2f p0 = corners.at(0)->pt;\n    cv::Point2f p1 = corners.at(width-1)->pt;\n    cv::Point2f p2 = corners.at(width)->pt;\n\n    if ((p1 - p0).cross(p2 - p0) < 0.0f)\n    {\n        for (int i = 0; i < height; ++i)\n        {\n            for (int j = 0; j < width / 2; ++j)\n            {\n                std::swap(corners.at(i * width + j), corners.at(i * width + width - j - 1));\n            }\n        }\n    }\n\n    p0 = corners.at(0)->pt;\n    p2 = corners.at(width)->pt;\n\n    // check if we need to rotate the board\n    if (p2.y < p0.y)\n    {\n        std::vector<ChessboardCornerPtr> outputCorners;\n        outputCorners.resize(corners.size());\n\n        for (int i = 0; i < height; ++i)\n        {\n            for (int j = 0; j < width; ++j)\n            {\n                outputCorners.at(i * width + j) = corners.at((height - i - 1) * width + width - j - 1);\n            }\n        }\n\n        corners = outputCorners;\n    }\n\n    return true;\n}\n\nvoid\nChessboard::getQuadrangleHypotheses(const std::vector< std::vector<cv::Point> >& contours,\n                                    std::vector< std::pair<float, int> >& quads,\n                                    int classId) const\n{\n    const float minAspectRatio = 0.2f;\n    const float maxAspectRatio = 5.0f;\n    const float minBoxSize = 10.0f;\n\n    for (size_t i = 0; i < contours.size(); ++i)\n    {\n        cv::RotatedRect box = cv::minAreaRect(contours.at(i));\n        float boxSize = std::max(box.size.width, box.size.height);\n        if (boxSize < minBoxSize)\n        {\n            continue;\n        }\n\n        float aspectRatio = box.size.width / std::max(box.size.height, 1.0f);\n\n        if (aspectRatio < minAspectRatio || aspectRatio > maxAspectRatio)\n        {\n            continue;\n        }\n\n        quads.push_back(std::pair<float, int>(boxSize, classId));\n    }\n}\n\nbool less_pred(const std::pair<float, int>& p1, const std::pair<float, int>& p2)\n{\n    return p1.first < p2.first;\n}\n\nvoid countClasses(const std::vector<std::pair<float, int> >& pairs, size_t idx1, size_t idx2, std::vector<int>& counts)\n{\n    counts.assign(2, 0);\n    for (size_t i = idx1; i != idx2; ++i)\n    {\n        counts[pairs[i].second]++;\n    }\n}\n\nbool\nChessboard::checkChessboard(const cv::Mat& image, cv::Size patternSize) const\n{\n    const int erosionCount = 1;\n    const float blackLevel = 20.f;\n    const float whiteLevel = 130.f;\n    const float blackWhiteGap = 70.f;\n\n    cv::Mat white = image.clone();\n\n    cv::Mat black = image.clone();\n\n    cv::erode(white, white, cv::Mat(), cv::Point(-1,-1), erosionCount);\n    cv::dilate(black, black, cv::Mat(), cv::Point(-1,-1), erosionCount);\n\n    cv::Mat thresh(image.rows, image.cols, CV_8UC1);\n\n    bool result = false;\n    for (float threshLevel = blackLevel; threshLevel < whiteLevel && !result; threshLevel += 20.0f)\n    {\n        cv::threshold(white, thresh, threshLevel + blackWhiteGap, 255, CV_THRESH_BINARY);\n\n        std::vector< std::vector<cv::Point> > contours;\n        std::vector<cv::Vec4i> hierarchy;\n\n        // Initialize contour retrieving routine\n        cv::findContours(thresh, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);\n\n        std::vector<std::pair<float, int> > quads;\n        getQuadrangleHypotheses(contours, quads, 1);\n\n        cv::threshold(black, thresh, threshLevel, 255, CV_THRESH_BINARY_INV);\n\n        cv::findContours(thresh, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);\n        getQuadrangleHypotheses(contours, quads, 0);\n\n        const size_t min_quads_count = patternSize.width * patternSize.height / 2;\n        std::sort(quads.begin(), quads.end(), less_pred);\n\n        // now check if there are many hypotheses with similar sizes\n        // do this by floodfill-style algorithm\n        const float sizeRelDev = 0.4f;\n\n        for (size_t i = 0; i < quads.size(); ++i)\n        {\n            size_t j = i + 1;\n            for (; j < quads.size(); ++j)\n            {\n                if (quads[j].first / quads[i].first > 1.0f + sizeRelDev)\n                {\n                    break;\n                }\n            }\n\n            if (j + 1 > min_quads_count + i)\n            {\n                // check the number of black and white squares\n                std::vector<int> counts;\n                countClasses(quads, i, j, counts);\n                const int blackCount = lroundf(ceilf(patternSize.width / 2.0f) * ceilf(patternSize.height / 2.0f));\n                const int whiteCount = lroundf(floorf(patternSize.width / 2.0f) * floorf(patternSize.height / 2.0f));\n                if (counts[0] < blackCount * 0.75f ||\n                    counts[1] < whiteCount * 0.75f)\n                {\n                    continue;\n                }\n                result = true;\n                break;\n            }\n        }\n    }\n\n    return result;\n}\n\nbool\nChessboard::checkBoardMonotony(std::vector<ChessboardCornerPtr>& corners,\n                               cv::Size patternSize)\n{\n    const float threshFactor = 0.2f;\n\n    Spline splineXY, splineYX;\n    splineXY.setLowBC(Spline::PARABOLIC_RUNOUT_BC);\n    splineXY.setHighBC(Spline::PARABOLIC_RUNOUT_BC);\n    splineYX.setLowBC(Spline::PARABOLIC_RUNOUT_BC);\n    splineYX.setHighBC(Spline::PARABOLIC_RUNOUT_BC);\n\n    // check if each row of corners approximates a cubic spline\n    for (int i = 0; i < patternSize.height; ++i)\n    {\n        splineXY.clear();\n        splineYX.clear();\n\n        cv::Point2f p[3];\n        p[0] = corners.at(i * patternSize.width)->pt;\n        p[1] = corners.at(i * patternSize.width + patternSize.width / 2)->pt;\n        p[2] = corners.at(i * patternSize.width + patternSize.width - 1)->pt;\n\n        for (int j = 0; j < 3; ++j)\n        {\n            splineXY.addPoint(p[j].x, p[j].y);\n            splineYX.addPoint(p[j].y, p[j].x);\n        }\n\n        for (int j = 1; j < patternSize.width - 1; ++j)\n        {\n            cv::Point2f& p_j = corners.at(i * patternSize.width + j)->pt;\n\n            float thresh = std::numeric_limits<float>::max();\n\n            // up-neighbor\n            if (i > 0)\n            {\n                cv::Point2f& neighbor = corners.at((i - 1) * patternSize.width + j)->pt;\n                thresh = fminf(thresh, cv::norm(neighbor - p_j));\n            }\n            // down-neighbor\n            if (i < patternSize.height - 1)\n            {\n                cv::Point2f& neighbor = corners.at((i + 1) * patternSize.width + j)->pt;\n                thresh = fminf(thresh, cv::norm(neighbor - p_j));\n            }\n            // left-neighbor\n            {\n                cv::Point2f& neighbor = corners.at(i * patternSize.width + j - 1)->pt;\n                thresh = fminf(thresh, cv::norm(neighbor - p_j));\n            }\n            // right-neighbor\n            {\n                cv::Point2f& neighbor = corners.at(i * patternSize.width + j + 1)->pt;\n                thresh = fminf(thresh, cv::norm(neighbor - p_j));\n            }\n\n            thresh *= threshFactor;\n\n            if (fminf(fabsf(splineXY(p_j.x) - p_j.y), fabsf(splineYX(p_j.y) - p_j.x)) > thresh)\n            {\n                return false;\n            }\n        }\n    }\n\n    // check if each column of corners approximates a cubic spline\n    for (int j = 0; j < patternSize.width; ++j)\n    {\n        splineXY.clear();\n        splineYX.clear();\n\n        cv::Point2f p[3];\n        p[0] = corners.at(j)->pt;\n        p[1] = corners.at(patternSize.height / 2 * patternSize.width + j)->pt;\n        p[2] = corners.at((patternSize.height - 1) * patternSize.width + j)->pt;\n\n        for (int i = 0; i < 3; ++i)\n        {\n            splineXY.addPoint(p[i].x, p[i].y);\n            splineYX.addPoint(p[i].y, p[i].x);\n        }\n\n        for (int i = 1; i < patternSize.height - 1; ++i)\n        {\n            cv::Point2f& p_i = corners.at(i * patternSize.width + j)->pt;\n\n            float thresh = std::numeric_limits<float>::max();\n\n            // up-neighbor\n            {\n                cv::Point2f& neighbor = corners.at((i - 1) * patternSize.width + j)->pt;\n                thresh = fminf(thresh, cv::norm(neighbor - p_i));\n            }\n            // down-neighbor\n            {\n                cv::Point2f& neighbor = corners.at((i + 1) * patternSize.width + j)->pt;\n                thresh = fminf(thresh, cv::norm(neighbor - p_i));\n            }\n            // left-neighbor\n            if (j > 0)\n            {\n                cv::Point2f& neighbor = corners.at(i * patternSize.width + j - 1)->pt;\n                thresh = fminf(thresh, cv::norm(neighbor - p_i));\n            }\n            // right-neighbor\n            if (j < patternSize.width - 1)\n            {\n                cv::Point2f& neighbor = corners.at(i * patternSize.width + j + 1)->pt;\n                thresh = fminf(thresh, cv::norm(neighbor - p_i));\n            }\n\n            thresh *= threshFactor;\n\n            if (fminf(fabsf(splineXY(p_i.x) - p_i.y), fabsf(splineYX(p_i.y) - p_i.x)) > thresh)\n            {\n                return false;\n            }\n        }\n    }\n\n    return true;\n}\n\nbool\nChessboard::matchCorners(ChessboardQuadPtr& quad1, int corner1,\n                         ChessboardQuadPtr& quad2, int corner2) const\n{\n    // First Check everything from the viewpoint of the\n    // current quad compute midpoints of \"parallel\" quad\n    // sides 1\n    float x1 = (quad1->corners[corner1]->pt.x + quad1->corners[(corner1+1)%4]->pt.x)/2;\n    float y1 = (quad1->corners[corner1]->pt.y + quad1->corners[(corner1+1)%4]->pt.y)/2;\n    float x2 = (quad1->corners[(corner1+2)%4]->pt.x + quad1->corners[(corner1+3)%4]->pt.x)/2;\n    float y2 = (quad1->corners[(corner1+2)%4]->pt.y + quad1->corners[(corner1+3)%4]->pt.y)/2;\n    // compute midpoints of \"parallel\" quad sides 2\n    float x3 = (quad1->corners[corner1]->pt.x + quad1->corners[(corner1+3)%4]->pt.x)/2;\n    float y3 = (quad1->corners[corner1]->pt.y + quad1->corners[(corner1+3)%4]->pt.y)/2;\n    float x4 = (quad1->corners[(corner1+1)%4]->pt.x + quad1->corners[(corner1+2)%4]->pt.x)/2;\n    float y4 = (quad1->corners[(corner1+1)%4]->pt.y + quad1->corners[(corner1+2)%4]->pt.y)/2;\n\n    // MARTIN: Heuristic\n    // For corner2 of quad2 to be considered,\n    // it needs to be on the same side of the two lines as\n    // corner1. This is given, if the cross product has\n    // the same sign for both computations below:\n    float a1 = x1 - x2;\n    float b1 = y1 - y2;\n    // the current corner\n    float c11 = quad1->corners[corner1]->pt.x - x2;\n    float d11 = quad1->corners[corner1]->pt.y - y2;\n    // the candidate corner\n    float c12 = quad2->corners[corner2]->pt.x - x2;\n    float d12 = quad2->corners[corner2]->pt.y - y2;\n    float sign11 = a1*d11 - c11*b1;\n    float sign12 = a1*d12 - c12*b1;\n\n    float a2 = x3 - x4;\n    float b2 = y3 - y4;\n    // the current corner\n    float c21 = quad1->corners[corner1]->pt.x - x4;\n    float d21 = quad1->corners[corner1]->pt.y - y4;\n    // the candidate corner\n    float c22 = quad2->corners[corner2]->pt.x - x4;\n    float d22 = quad2->corners[corner2]->pt.y - y4;\n    float sign21 = a2*d21 - c21*b2;\n    float sign22 = a2*d22 - c22*b2;\n\n    // Also make shure that two border quads of the same row or\n    // column don't link. Check from the current corner's view,\n    // whether the corner diagonal from the candidate corner\n    // is also on the same side of the two lines as the current\n    // corner and the candidate corner.\n    float c13 = quad2->corners[(corner2+2)%4]->pt.x - x2;\n    float d13 = quad2->corners[(corner2+2)%4]->pt.y - y2;\n    float c23 = quad2->corners[(corner2+2)%4]->pt.x - x4;\n    float d23 = quad2->corners[(corner2+2)%4]->pt.y - y4;\n    float sign13 = a1*d13 - c13*b1;\n    float sign23 = a2*d23 - c23*b2;\n\n\n    // Second: Then check everything from the viewpoint of\n    // the candidate quad. Compute midpoints of \"parallel\"\n    // quad sides 1\n    float u1 = (quad2->corners[corner2]->pt.x + quad2->corners[(corner2+1)%4]->pt.x)/2;\n    float v1 = (quad2->corners[corner2]->pt.y + quad2->corners[(corner2+1)%4]->pt.y)/2;\n    float u2 = (quad2->corners[(corner2+2)%4]->pt.x + quad2->corners[(corner2+3)%4]->pt.x)/2;\n    float v2 = (quad2->corners[(corner2+2)%4]->pt.y + quad2->corners[(corner2+3)%4]->pt.y)/2;\n    // compute midpoints of \"parallel\" quad sides 2\n    float u3 = (quad2->corners[corner2]->pt.x + quad2->corners[(corner2+3)%4]->pt.x)/2;\n    float v3 = (quad2->corners[corner2]->pt.y + quad2->corners[(corner2+3)%4]->pt.y)/2;\n    float u4 = (quad2->corners[(corner2+1)%4]->pt.x + quad2->corners[(corner2+2)%4]->pt.x)/2;\n    float v4 = (quad2->corners[(corner2+1)%4]->pt.y + quad2->corners[(corner2+2)%4]->pt.y)/2;\n\n    // MARTIN: Heuristic\n    // For corner2 of quad2 to be considered,\n    // it needs to be on the same side of the two lines as\n    // corner1. This is given, if the cross product has\n    // the same sign for both computations below:\n    float a3 = u1 - u2;\n    float b3 = v1 - v2;\n    // the current corner\n    float c31 = quad1->corners[corner1]->pt.x - u2;\n    float d31 = quad1->corners[corner1]->pt.y - v2;\n    // the candidate corner\n    float c32 = quad2->corners[corner2]->pt.x - u2;\n    float d32 = quad2->corners[corner2]->pt.y - v2;\n    float sign31 = a3*d31-c31*b3;\n    float sign32 = a3*d32-c32*b3;\n\n    float a4 = u3 - u4;\n    float b4 = v3 - v4;\n    // the current corner\n    float c41 = quad1->corners[corner1]->pt.x - u4;\n    float d41 = quad1->corners[corner1]->pt.y - v4;\n    // the candidate corner\n    float c42 = quad2->corners[corner2]->pt.x - u4;\n    float d42 = quad2->corners[corner2]->pt.y - v4;\n    float sign41 = a4*d41-c41*b4;\n    float sign42 = a4*d42-c42*b4;\n\n    // Also make sure that two border quads of the same row or\n    // column don't link. Check from the candidate corner's view,\n    // whether the corner diagonal from the current corner\n    // is also on the same side of the two lines as the current\n    // corner and the candidate corner.\n    float c33 = quad1->corners[(corner1+2)%4]->pt.x - u2;\n    float d33 = quad1->corners[(corner1+2)%4]->pt.y - v2;\n    float c43 = quad1->corners[(corner1+2)%4]->pt.x - u4;\n    float d43 = quad1->corners[(corner1+2)%4]->pt.y - v4;\n    float sign33 = a3*d33-c33*b3;\n    float sign43 = a4*d43-c43*b4;\n\n\n    // This time we also need to make shure, that no quad\n    // is linked to a quad of another dilation run which\n    // may lie INSIDE it!!!\n    // Third: Therefore check everything from the viewpoint\n    // of the current quad compute midpoints of \"parallel\"\n    // quad sides 1\n    float x5 = quad1->corners[corner1]->pt.x;\n    float y5 = quad1->corners[corner1]->pt.y;\n    float x6 = quad1->corners[(corner1+1)%4]->pt.x;\n    float y6 = quad1->corners[(corner1+1)%4]->pt.y;\n    // compute midpoints of \"parallel\" quad sides 2\n    float x7 = x5;\n    float y7 = y5;\n    float x8 = quad1->corners[(corner1+3)%4]->pt.x;\n    float y8 = quad1->corners[(corner1+3)%4]->pt.y;\n\n    // MARTIN: Heuristic\n    // For corner2 of quad2 to be considered,\n    // it needs to be on the other side of the two lines than\n    // corner1. This is given, if the cross product has\n    // a different sign for both computations below:\n    float a5 = x6 - x5;\n    float b5 = y6 - y5;\n    // the current corner\n    float c51 = quad1->corners[(corner1+2)%4]->pt.x - x5;\n    float d51 = quad1->corners[(corner1+2)%4]->pt.y - y5;\n    // the candidate corner\n    float c52 = quad2->corners[corner2]->pt.x - x5;\n    float d52 = quad2->corners[corner2]->pt.y - y5;\n    float sign51 = a5*d51 - c51*b5;\n    float sign52 = a5*d52 - c52*b5;\n\n    float a6 = x8 - x7;\n    float b6 = y8 - y7;\n    // the current corner\n    float c61 = quad1->corners[(corner1+2)%4]->pt.x - x7;\n    float d61 = quad1->corners[(corner1+2)%4]->pt.y - y7;\n    // the candidate corner\n    float c62 = quad2->corners[corner2]->pt.x - x7;\n    float d62 = quad2->corners[corner2]->pt.y - y7;\n    float sign61 = a6*d61 - c61*b6;\n    float sign62 = a6*d62 - c62*b6;\n\n\n    // Fourth: Then check everything from the viewpoint of\n    // the candidate quad compute midpoints of \"parallel\"\n    // quad sides 1\n    float u5 = quad2->corners[corner2]->pt.x;\n    float v5 = quad2->corners[corner2]->pt.y;\n    float u6 = quad2->corners[(corner2+1)%4]->pt.x;\n    float v6 = quad2->corners[(corner2+1)%4]->pt.y;\n    // compute midpoints of \"parallel\" quad sides 2\n    float u7 = u5;\n    float v7 = v5;\n    float u8 = quad2->corners[(corner2+3)%4]->pt.x;\n    float v8 = quad2->corners[(corner2+3)%4]->pt.y;\n\n    // MARTIN: Heuristic\n    // For corner2 of quad2 to be considered,\n    // it needs to be on the other side of the two lines than\n    // corner1. This is given, if the cross product has\n    // a different sign for both computations below:\n    float a7 = u6 - u5;\n    float b7 = v6 - v5;\n    // the current corner\n    float c71 = quad1->corners[corner1]->pt.x - u5;\n    float d71 = quad1->corners[corner1]->pt.y - v5;\n    // the candidate corner\n    float c72 = quad2->corners[(corner2+2)%4]->pt.x - u5;\n    float d72 = quad2->corners[(corner2+2)%4]->pt.y - v5;\n    float sign71 = a7*d71-c71*b7;\n    float sign72 = a7*d72-c72*b7;\n\n    float a8 = u8 - u7;\n    float b8 = v8 - v7;\n    // the current corner\n    float c81 = quad1->corners[corner1]->pt.x - u7;\n    float d81 = quad1->corners[corner1]->pt.y - v7;\n    // the candidate corner\n    float c82 = quad2->corners[(corner2+2)%4]->pt.x - u7;\n    float d82 = quad2->corners[(corner2+2)%4]->pt.y - v7;\n    float sign81 = a8*d81-c81*b8;\n    float sign82 = a8*d82-c82*b8;\n\n    // Check whether conditions are fulfilled\n    if (((sign11 < 0 && sign12 < 0) || (sign11 > 0 && sign12 > 0))  &&\n        ((sign21 < 0 && sign22 < 0) || (sign21 > 0 && sign22 > 0))  &&\n        ((sign31 < 0 && sign32 < 0) || (sign31 > 0 && sign32 > 0))  &&\n        ((sign41 < 0 && sign42 < 0) || (sign41 > 0 && sign42 > 0))  &&\n        ((sign11 < 0 && sign13 < 0) || (sign11 > 0 && sign13 > 0))  &&\n        ((sign21 < 0 && sign23 < 0) || (sign21 > 0 && sign23 > 0))  &&\n        ((sign31 < 0 && sign33 < 0) || (sign31 > 0 && sign33 > 0))  &&\n        ((sign41 < 0 && sign43 < 0) || (sign41 > 0 && sign43 > 0))  &&\n        ((sign51 < 0 && sign52 > 0) || (sign51 > 0 && sign52 < 0))  &&\n        ((sign61 < 0 && sign62 > 0) || (sign61 > 0 && sign62 < 0))  &&\n        ((sign71 < 0 && sign72 > 0) || (sign71 > 0 && sign72 < 0))  &&\n        ((sign81 < 0 && sign82 > 0) || (sign81 > 0 && sign82 < 0)))\n    {\n        return true;\n    }\n    else\n    {\n        return false;\n    }\n}\n\n}\n"
  },
  {
    "path": "src/utils/camodocal/src/gpl/EigenQuaternionParameterization.cc",
    "content": "#include \"camodocal/gpl/EigenQuaternionParameterization.h\"\n\n#include <cmath>\n\nnamespace camodocal\n{\n\nbool\nEigenQuaternionParameterization::Plus(const double* x,\n                                      const double* delta,\n                                      double* x_plus_delta) const\n{\n    const double norm_delta =\n        sqrt(delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]);\n    if (norm_delta > 0.0)\n    {\n        const double sin_delta_by_delta = (sin(norm_delta) / norm_delta);\n        double q_delta[4];\n        q_delta[0] = sin_delta_by_delta * delta[0];\n        q_delta[1] = sin_delta_by_delta * delta[1];\n        q_delta[2] = sin_delta_by_delta * delta[2];\n        q_delta[3] = cos(norm_delta);\n        EigenQuaternionProduct(q_delta, x, x_plus_delta);\n    }\n    else\n    {\n        for (int i = 0; i < 4; ++i)\n        {\n            x_plus_delta[i] = x[i];\n        }\n    }\n    return true;\n}\n\nbool\nEigenQuaternionParameterization::ComputeJacobian(const double* x,\n                                                 double* jacobian) const\n{\n    jacobian[0] =  x[3]; jacobian[1]  =  x[2]; jacobian[2]  = -x[1];  // NOLINT\n    jacobian[3] = -x[2]; jacobian[4]  =  x[3]; jacobian[5]  =  x[0];  // NOLINT\n    jacobian[6] =  x[1]; jacobian[7] = -x[0]; jacobian[8] =  x[3];  // NOLINT\n    jacobian[9] = -x[0]; jacobian[10]  = -x[1]; jacobian[11]  = -x[2];  // NOLINT\n    return true;\n}\n\n}\n"
  },
  {
    "path": "src/utils/camodocal/src/gpl/gpl.cc",
    "content": "#include \"camodocal/gpl/gpl.h\"\r\n\r\n#include <set>\r\n#ifdef _WIN32\r\n#include <winsock.h>\r\n#else\r\n#include <time.h>\r\n#endif\r\n\r\n\r\n// source: https://stackoverflow.com/questions/5167269/clock-gettime-alternative-in-mac-os-x\r\n#ifdef __APPLE__\r\n#include <mach/mach_time.h>\r\n#define ORWL_NANO (+1.0E-9)\r\n#define ORWL_GIGA UINT64_C(1000000000)\r\n\r\nstatic double orwl_timebase = 0.0;\r\nstatic uint64_t orwl_timestart = 0;\r\n\r\nstruct timespec orwl_gettime(void) {\r\n  // be more careful in a multithreaded environement\r\n  if (!orwl_timestart) {\r\n    mach_timebase_info_data_t tb = { 0 };\r\n    mach_timebase_info(&tb);\r\n    orwl_timebase = tb.numer;\r\n    orwl_timebase /= tb.denom;\r\n    orwl_timestart = mach_absolute_time();\r\n  }\r\n  struct timespec t;\r\n  double diff = (mach_absolute_time() - orwl_timestart) * orwl_timebase;\r\n  t.tv_sec = diff * ORWL_NANO;\r\n  t.tv_nsec = diff - (t.tv_sec * ORWL_GIGA);\r\n  return t;\r\n}\r\n#endif // __APPLE__\r\n\r\n\r\nconst double WGS84_A = 6378137.0;\r\nconst double WGS84_ECCSQ = 0.00669437999013;\r\n\r\n// Windows lacks fminf\r\n#ifndef fminf\r\n#define fminf(x, y) (((x) < (y)) ? (x) : (y))\r\n#endif\r\n\r\nnamespace camodocal\r\n{\r\n\r\ndouble hypot3(double x, double y, double z)\r\n{\r\n\treturn sqrt(square(x) + square(y) + square(z));\r\n}\r\n\r\nfloat hypot3f(float x, float y, float z)\r\n{\r\n\treturn sqrtf(square(x) + square(y) + square(z));\r\n}\r\n\r\ndouble d2r(double deg)\r\n{\r\n\treturn deg / 180.0 * M_PI;\r\n}\r\n\r\nfloat d2r(float deg)\r\n{\r\n\treturn deg / 180.0f * M_PI;\r\n}\r\n\r\ndouble r2d(double rad)\r\n{\r\n\treturn rad / M_PI * 180.0;\r\n}\r\n\r\nfloat r2d(float rad)\r\n{\r\n\treturn rad / M_PI * 180.0f;\r\n}\r\n\r\ndouble sinc(double theta)\r\n{\r\n    return sin(theta) / theta;\r\n}\r\n\r\n#ifdef _WIN32\r\n#include <sys/timeb.h>\r\n#include <sys/types.h>\r\n#include <winsock.h>\r\nLARGE_INTEGER\r\ngetFILETIMEoffset()\r\n{\r\n    SYSTEMTIME s;\r\n    FILETIME f;\r\n    LARGE_INTEGER t;\r\n\r\n    s.wYear = 1970;\r\n    s.wMonth = 1;\r\n    s.wDay = 1;\r\n    s.wHour = 0;\r\n    s.wMinute = 0;\r\n    s.wSecond = 0;\r\n    s.wMilliseconds = 0;\r\n    SystemTimeToFileTime(&s, &f);\r\n    t.QuadPart = f.dwHighDateTime;\r\n    t.QuadPart <<= 32;\r\n    t.QuadPart |= f.dwLowDateTime;\r\n    return (t);\r\n}\r\n\r\nint\r\nclock_gettime(int X, struct timespec *tp)\r\n{\r\n    LARGE_INTEGER           t;\r\n    FILETIME            f;\r\n    double                  microseconds;\r\n    static LARGE_INTEGER    offset;\r\n    static double           frequencyToMicroseconds;\r\n    static int              initialized = 0;\r\n    static BOOL             usePerformanceCounter = 0;\r\n\r\n    if (!initialized) {\r\n        LARGE_INTEGER performanceFrequency;\r\n        initialized = 1;\r\n        usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency);\r\n        if (usePerformanceCounter) {\r\n            QueryPerformanceCounter(&offset);\r\n            frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.;\r\n        } else {\r\n            offset = getFILETIMEoffset();\r\n            frequencyToMicroseconds = 10.;\r\n        }\r\n    }\r\n    if (usePerformanceCounter) QueryPerformanceCounter(&t);\r\n    else {\r\n        GetSystemTimeAsFileTime(&f);\r\n        t.QuadPart = f.dwHighDateTime;\r\n        t.QuadPart <<= 32;\r\n        t.QuadPart |= f.dwLowDateTime;\r\n    }\r\n\r\n    t.QuadPart -= offset.QuadPart;\r\n    microseconds = (double)t.QuadPart / frequencyToMicroseconds;\r\n    t.QuadPart = microseconds;\r\n    tp->tv_sec = t.QuadPart / 1000000;\r\n    tp->tv_nsec = (t.QuadPart % 1000000) * 1000;\r\n    return (0);\r\n}\r\n#endif\r\n\r\nunsigned long long timeInMicroseconds(void)\r\n{\r\n\t struct timespec tp;\r\n#ifdef __APPLE__\r\n     tp = orwl_gettime();\r\n#else\r\n\t clock_gettime(CLOCK_REALTIME, &tp);\r\n#endif\r\n\r\n\t return tp.tv_sec * 1000000 + tp.tv_nsec / 1000;\r\n}\r\n\r\ndouble timeInSeconds(void)\r\n{\r\n    struct timespec tp;\r\n#ifdef __APPLE__\r\n     tp = orwl_gettime();\r\n#else\r\n\t clock_gettime(CLOCK_REALTIME, &tp);\r\n#endif\r\n\r\n\t return static_cast<double>(tp.tv_sec) +\r\n\t\t\t static_cast<double>(tp.tv_nsec) / 1000000000.0;\r\n}\r\n\r\nfloat colormapAutumn[128][3] =\r\n{\r\n\t\t{1.0f,0.f,0.f},\r\n\t\t{1.0f,0.007874f,0.f},\r\n\t\t{1.0f,0.015748f,0.f},\r\n\t\t{1.0f,0.023622f,0.f},\r\n\t\t{1.0f,0.031496f,0.f},\r\n\t\t{1.0f,0.03937f,0.f},\r\n\t\t{1.0f,0.047244f,0.f},\r\n\t\t{1.0f,0.055118f,0.f},\r\n\t\t{1.0f,0.062992f,0.f},\r\n\t\t{1.0f,0.070866f,0.f},\r\n\t\t{1.0f,0.07874f,0.f},\r\n\t\t{1.0f,0.086614f,0.f},\r\n\t\t{1.0f,0.094488f,0.f},\r\n\t\t{1.0f,0.10236f,0.f},\r\n\t\t{1.0f,0.11024f,0.f},\r\n\t\t{1.0f,0.11811f,0.f},\r\n\t\t{1.0f,0.12598f,0.f},\r\n\t\t{1.0f,0.13386f,0.f},\r\n\t\t{1.0f,0.14173f,0.f},\r\n\t\t{1.0f,0.14961f,0.f},\r\n\t\t{1.0f,0.15748f,0.f},\r\n\t\t{1.0f,0.16535f,0.f},\r\n\t\t{1.0f,0.17323f,0.f},\r\n\t\t{1.0f,0.1811f,0.f},\r\n\t\t{1.0f,0.18898f,0.f},\r\n\t\t{1.0f,0.19685f,0.f},\r\n\t\t{1.0f,0.20472f,0.f},\r\n\t\t{1.0f,0.2126f,0.f},\r\n\t\t{1.0f,0.22047f,0.f},\r\n\t\t{1.0f,0.22835f,0.f},\r\n\t\t{1.0f,0.23622f,0.f},\r\n\t\t{1.0f,0.24409f,0.f},\r\n\t\t{1.0f,0.25197f,0.f},\r\n\t\t{1.0f,0.25984f,0.f},\r\n\t\t{1.0f,0.26772f,0.f},\r\n\t\t{1.0f,0.27559f,0.f},\r\n\t\t{1.0f,0.28346f,0.f},\r\n\t\t{1.0f,0.29134f,0.f},\r\n\t\t{1.0f,0.29921f,0.f},\r\n\t\t{1.0f,0.30709f,0.f},\r\n\t\t{1.0f,0.31496f,0.f},\r\n\t\t{1.0f,0.32283f,0.f},\r\n\t\t{1.0f,0.33071f,0.f},\r\n\t\t{1.0f,0.33858f,0.f},\r\n\t\t{1.0f,0.34646f,0.f},\r\n\t\t{1.0f,0.35433f,0.f},\r\n\t\t{1.0f,0.3622f,0.f},\r\n\t\t{1.0f,0.37008f,0.f},\r\n\t\t{1.0f,0.37795f,0.f},\r\n\t\t{1.0f,0.38583f,0.f},\r\n\t\t{1.0f,0.3937f,0.f},\r\n\t\t{1.0f,0.40157f,0.f},\r\n\t\t{1.0f,0.40945f,0.f},\r\n\t\t{1.0f,0.41732f,0.f},\r\n\t\t{1.0f,0.4252f,0.f},\r\n\t\t{1.0f,0.43307f,0.f},\r\n\t\t{1.0f,0.44094f,0.f},\r\n\t\t{1.0f,0.44882f,0.f},\r\n\t\t{1.0f,0.45669f,0.f},\r\n\t\t{1.0f,0.46457f,0.f},\r\n\t\t{1.0f,0.47244f,0.f},\r\n\t\t{1.0f,0.48031f,0.f},\r\n\t\t{1.0f,0.48819f,0.f},\r\n\t\t{1.0f,0.49606f,0.f},\r\n\t\t{1.0f,0.50394f,0.f},\r\n\t\t{1.0f,0.51181f,0.f},\r\n\t\t{1.0f,0.51969f,0.f},\r\n\t\t{1.0f,0.52756f,0.f},\r\n\t\t{1.0f,0.53543f,0.f},\r\n\t\t{1.0f,0.54331f,0.f},\r\n\t\t{1.0f,0.55118f,0.f},\r\n\t\t{1.0f,0.55906f,0.f},\r\n\t\t{1.0f,0.56693f,0.f},\r\n\t\t{1.0f,0.5748f,0.f},\r\n\t\t{1.0f,0.58268f,0.f},\r\n\t\t{1.0f,0.59055f,0.f},\r\n\t\t{1.0f,0.59843f,0.f},\r\n\t\t{1.0f,0.6063f,0.f},\r\n\t\t{1.0f,0.61417f,0.f},\r\n\t\t{1.0f,0.62205f,0.f},\r\n\t\t{1.0f,0.62992f,0.f},\r\n\t\t{1.0f,0.6378f,0.f},\r\n\t\t{1.0f,0.64567f,0.f},\r\n\t\t{1.0f,0.65354f,0.f},\r\n\t\t{1.0f,0.66142f,0.f},\r\n\t\t{1.0f,0.66929f,0.f},\r\n\t\t{1.0f,0.67717f,0.f},\r\n\t\t{1.0f,0.68504f,0.f},\r\n\t\t{1.0f,0.69291f,0.f},\r\n\t\t{1.0f,0.70079f,0.f},\r\n\t\t{1.0f,0.70866f,0.f},\r\n\t\t{1.0f,0.71654f,0.f},\r\n\t\t{1.0f,0.72441f,0.f},\r\n\t\t{1.0f,0.73228f,0.f},\r\n\t\t{1.0f,0.74016f,0.f},\r\n\t\t{1.0f,0.74803f,0.f},\r\n\t\t{1.0f,0.75591f,0.f},\r\n\t\t{1.0f,0.76378f,0.f},\r\n\t\t{1.0f,0.77165f,0.f},\r\n\t\t{1.0f,0.77953f,0.f},\r\n\t\t{1.0f,0.7874f,0.f},\r\n\t\t{1.0f,0.79528f,0.f},\r\n\t\t{1.0f,0.80315f,0.f},\r\n\t\t{1.0f,0.81102f,0.f},\r\n\t\t{1.0f,0.8189f,0.f},\r\n\t\t{1.0f,0.82677f,0.f},\r\n\t\t{1.0f,0.83465f,0.f},\r\n\t\t{1.0f,0.84252f,0.f},\r\n\t\t{1.0f,0.85039f,0.f},\r\n\t\t{1.0f,0.85827f,0.f},\r\n\t\t{1.0f,0.86614f,0.f},\r\n\t\t{1.0f,0.87402f,0.f},\r\n\t\t{1.0f,0.88189f,0.f},\r\n\t\t{1.0f,0.88976f,0.f},\r\n\t\t{1.0f,0.89764f,0.f},\r\n\t\t{1.0f,0.90551f,0.f},\r\n\t\t{1.0f,0.91339f,0.f},\r\n\t\t{1.0f,0.92126f,0.f},\r\n\t\t{1.0f,0.92913f,0.f},\r\n\t\t{1.0f,0.93701f,0.f},\r\n\t\t{1.0f,0.94488f,0.f},\r\n\t\t{1.0f,0.95276f,0.f},\r\n\t\t{1.0f,0.96063f,0.f},\r\n\t\t{1.0f,0.9685f,0.f},\r\n\t\t{1.0f,0.97638f,0.f},\r\n\t\t{1.0f,0.98425f,0.f},\r\n\t\t{1.0f,0.99213f,0.f},\r\n\t\t{1.0f,1.0f,0.0f}\r\n};\r\n\r\n\r\nfloat colormapJet[128][3] =\r\n{\r\n\t\t{0.0f,0.0f,0.53125f},\r\n\t\t{0.0f,0.0f,0.5625f},\r\n\t\t{0.0f,0.0f,0.59375f},\r\n\t\t{0.0f,0.0f,0.625f},\r\n\t\t{0.0f,0.0f,0.65625f},\r\n\t\t{0.0f,0.0f,0.6875f},\r\n\t\t{0.0f,0.0f,0.71875f},\r\n\t\t{0.0f,0.0f,0.75f},\r\n\t\t{0.0f,0.0f,0.78125f},\r\n\t\t{0.0f,0.0f,0.8125f},\r\n\t\t{0.0f,0.0f,0.84375f},\r\n\t\t{0.0f,0.0f,0.875f},\r\n\t\t{0.0f,0.0f,0.90625f},\r\n\t\t{0.0f,0.0f,0.9375f},\r\n\t\t{0.0f,0.0f,0.96875f},\r\n\t\t{0.0f,0.0f,1.0f},\r\n\t\t{0.0f,0.03125f,1.0f},\r\n\t\t{0.0f,0.0625f,1.0f},\r\n\t\t{0.0f,0.09375f,1.0f},\r\n\t\t{0.0f,0.125f,1.0f},\r\n\t\t{0.0f,0.15625f,1.0f},\r\n\t\t{0.0f,0.1875f,1.0f},\r\n\t\t{0.0f,0.21875f,1.0f},\r\n\t\t{0.0f,0.25f,1.0f},\r\n\t\t{0.0f,0.28125f,1.0f},\r\n\t\t{0.0f,0.3125f,1.0f},\r\n\t\t{0.0f,0.34375f,1.0f},\r\n\t\t{0.0f,0.375f,1.0f},\r\n\t\t{0.0f,0.40625f,1.0f},\r\n\t\t{0.0f,0.4375f,1.0f},\r\n\t\t{0.0f,0.46875f,1.0f},\r\n\t\t{0.0f,0.5f,1.0f},\r\n\t\t{0.0f,0.53125f,1.0f},\r\n\t\t{0.0f,0.5625f,1.0f},\r\n\t\t{0.0f,0.59375f,1.0f},\r\n\t\t{0.0f,0.625f,1.0f},\r\n\t\t{0.0f,0.65625f,1.0f},\r\n\t\t{0.0f,0.6875f,1.0f},\r\n\t\t{0.0f,0.71875f,1.0f},\r\n\t\t{0.0f,0.75f,1.0f},\r\n\t\t{0.0f,0.78125f,1.0f},\r\n\t\t{0.0f,0.8125f,1.0f},\r\n\t\t{0.0f,0.84375f,1.0f},\r\n\t\t{0.0f,0.875f,1.0f},\r\n\t\t{0.0f,0.90625f,1.0f},\r\n\t\t{0.0f,0.9375f,1.0f},\r\n\t\t{0.0f,0.96875f,1.0f},\r\n\t\t{0.0f,1.0f,1.0f},\r\n\t\t{0.03125f,1.0f,0.96875f},\r\n\t\t{0.0625f,1.0f,0.9375f},\r\n\t\t{0.09375f,1.0f,0.90625f},\r\n\t\t{0.125f,1.0f,0.875f},\r\n\t\t{0.15625f,1.0f,0.84375f},\r\n\t\t{0.1875f,1.0f,0.8125f},\r\n\t\t{0.21875f,1.0f,0.78125f},\r\n\t\t{0.25f,1.0f,0.75f},\r\n\t\t{0.28125f,1.0f,0.71875f},\r\n\t\t{0.3125f,1.0f,0.6875f},\r\n\t\t{0.34375f,1.0f,0.65625f},\r\n\t\t{0.375f,1.0f,0.625f},\r\n\t\t{0.40625f,1.0f,0.59375f},\r\n\t\t{0.4375f,1.0f,0.5625f},\r\n\t\t{0.46875f,1.0f,0.53125f},\r\n\t\t{0.5f,1.0f,0.5f},\r\n\t\t{0.53125f,1.0f,0.46875f},\r\n\t\t{0.5625f,1.0f,0.4375f},\r\n\t\t{0.59375f,1.0f,0.40625f},\r\n\t\t{0.625f,1.0f,0.375f},\r\n\t\t{0.65625f,1.0f,0.34375f},\r\n\t\t{0.6875f,1.0f,0.3125f},\r\n\t\t{0.71875f,1.0f,0.28125f},\r\n\t\t{0.75f,1.0f,0.25f},\r\n\t\t{0.78125f,1.0f,0.21875f},\r\n\t\t{0.8125f,1.0f,0.1875f},\r\n\t\t{0.84375f,1.0f,0.15625f},\r\n\t\t{0.875f,1.0f,0.125f},\r\n\t\t{0.90625f,1.0f,0.09375f},\r\n\t\t{0.9375f,1.0f,0.0625f},\r\n\t\t{0.96875f,1.0f,0.03125f},\r\n\t\t{1.0f,1.0f,0.0f},\r\n\t\t{1.0f,0.96875f,0.0f},\r\n\t\t{1.0f,0.9375f,0.0f},\r\n\t\t{1.0f,0.90625f,0.0f},\r\n\t\t{1.0f,0.875f,0.0f},\r\n\t\t{1.0f,0.84375f,0.0f},\r\n\t\t{1.0f,0.8125f,0.0f},\r\n\t\t{1.0f,0.78125f,0.0f},\r\n\t\t{1.0f,0.75f,0.0f},\r\n\t\t{1.0f,0.71875f,0.0f},\r\n\t\t{1.0f,0.6875f,0.0f},\r\n\t\t{1.0f,0.65625f,0.0f},\r\n\t\t{1.0f,0.625f,0.0f},\r\n\t\t{1.0f,0.59375f,0.0f},\r\n\t\t{1.0f,0.5625f,0.0f},\r\n\t\t{1.0f,0.53125f,0.0f},\r\n\t\t{1.0f,0.5f,0.0f},\r\n\t\t{1.0f,0.46875f,0.0f},\r\n\t\t{1.0f,0.4375f,0.0f},\r\n\t\t{1.0f,0.40625f,0.0f},\r\n\t\t{1.0f,0.375f,0.0f},\r\n\t\t{1.0f,0.34375f,0.0f},\r\n\t\t{1.0f,0.3125f,0.0f},\r\n\t\t{1.0f,0.28125f,0.0f},\r\n\t\t{1.0f,0.25f,0.0f},\r\n\t\t{1.0f,0.21875f,0.0f},\r\n\t\t{1.0f,0.1875f,0.0f},\r\n\t\t{1.0f,0.15625f,0.0f},\r\n\t\t{1.0f,0.125f,0.0f},\r\n\t\t{1.0f,0.09375f,0.0f},\r\n\t\t{1.0f,0.0625f,0.0f},\r\n\t\t{1.0f,0.03125f,0.0f},\r\n\t\t{1.0f,0.0f,0.0f},\r\n\t\t{0.96875f,0.0f,0.0f},\r\n\t\t{0.9375f,0.0f,0.0f},\r\n\t\t{0.90625f,0.0f,0.0f},\r\n\t\t{0.875f,0.0f,0.0f},\r\n\t\t{0.84375f,0.0f,0.0f},\r\n\t\t{0.8125f,0.0f,0.0f},\r\n\t\t{0.78125f,0.0f,0.0f},\r\n\t\t{0.75f,0.0f,0.0f},\r\n\t\t{0.71875f,0.0f,0.0f},\r\n\t\t{0.6875f,0.0f,0.0f},\r\n\t\t{0.65625f,0.0f,0.0f},\r\n\t\t{0.625f,0.0f,0.0f},\r\n\t\t{0.59375f,0.0f,0.0f},\r\n\t\t{0.5625f,0.0f,0.0f},\r\n\t\t{0.53125f,0.0f,0.0f},\r\n\t\t{0.5f,0.0f,0.0f}\r\n};\r\n\r\nvoid colorDepthImage(cv::Mat& imgDepth, cv::Mat& imgColoredDepth,\r\n\t\t\t\t\t float minRange, float maxRange)\r\n{\r\n\timgColoredDepth = cv::Mat::zeros(imgDepth.size(), CV_8UC3);\r\n\r\n\tfor (int i = 0; i < imgColoredDepth.rows; ++i)\r\n\t{\r\n\t\tconst float* depth = imgDepth.ptr<float>(i);\r\n\t\tunsigned char* pixel = imgColoredDepth.ptr<unsigned char>(i);\r\n\t\tfor (int j = 0; j < imgColoredDepth.cols; ++j)\r\n\t\t{\r\n\t\t\tif (depth[j] != 0)\r\n\t\t\t{\r\n\t\t\t\tint idx = fminf(depth[j] - minRange, maxRange - minRange) / (maxRange - minRange) * 127.0f;\r\n\t\t\t\tidx = 127 - idx;\r\n\r\n\t\t\t\tpixel[0] = colormapJet[idx][2] * 255.0f;\r\n\t\t\t\tpixel[1] = colormapJet[idx][1] * 255.0f;\r\n\t\t\t\tpixel[2] = colormapJet[idx][0] * 255.0f;\r\n\t\t\t}\r\n\r\n\t\t\tpixel += 3;\r\n\t\t}\r\n\t}\r\n}\r\n\r\nbool colormap(const std::string& name, unsigned char idx,\r\n\t\t\t  float& r, float& g, float& b)\r\n{\r\n\tif (name.compare(\"jet\") == 0)\r\n\t{\r\n\t\tfloat* color = colormapJet[idx];\r\n\r\n\t\tr = color[0];\r\n\t\tg = color[1];\r\n\t\tb = color[2];\r\n\r\n\t\treturn true;\r\n\t}\r\n\telse if (name.compare(\"autumn\") == 0)\r\n\t{\r\n\t\tfloat* color = colormapAutumn[idx];\r\n\r\n\t\tr = color[0];\r\n\t\tg = color[1];\r\n\t\tb = color[2];\r\n\r\n\t\treturn true;\r\n\t}\r\n\r\n\treturn false;\r\n}\r\n\r\nstd::vector<cv::Point2i> bresLine(int x0, int y0, int x1, int y1)\r\n{\r\n\t// Bresenham's line algorithm\r\n\t// Find cells intersected by line between (x0,y0) and (x1,y1)\r\n\r\n\tstd::vector<cv::Point2i> cells;\r\n\r\n\tint dx = std::abs(x1 - x0);\r\n\tint dy = std::abs(y1 - y0);\r\n\r\n\tint sx = (x0 < x1) ? 1 : -1;\r\n\tint sy = (y0 < y1) ? 1 : -1;\r\n\r\n\tint err = dx - dy;\r\n\r\n\twhile (1)\r\n\t{\r\n\t\tcells.push_back(cv::Point2i(x0, y0));\r\n\r\n\t\tif (x0 == x1 && y0 == y1)\r\n\t\t{\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\tint e2 = 2 * err;\r\n\t\tif (e2 > -dy)\r\n\t\t{\r\n\t\t\terr -= dy;\r\n\t\t\tx0 += sx;\r\n\t\t}\r\n\t\tif (e2 < dx)\r\n\t\t{\r\n\t\t\terr += dx;\r\n\t\t\ty0 += sy;\r\n\t\t}\r\n\t}\r\n\r\n\treturn cells;\r\n}\r\n\r\nstd::vector<cv::Point2i> bresCircle(int x0, int y0, int r)\r\n{\r\n\t// Bresenham's circle algorithm\r\n\t// Find cells intersected by circle with center (x0,y0) and radius r\r\n\r\n\tstd::vector< std::vector<bool> > mask(2 * r + 1);\r\n\t\r\n\tfor (int i = 0; i < 2 * r + 1; ++i)\r\n\t{\r\n\t\tmask[i].resize(2 * r + 1);\r\n\t\tfor (int j = 0; j < 2 * r + 1; ++j)\r\n\t\t{\r\n\t\t\tmask[i][j] = false;\r\n\t\t}\r\n\t}\r\n\r\n\tint f = 1 - r;\r\n\tint ddF_x = 1;\r\n\tint ddF_y = -2 * r;\r\n\tint x = 0;\r\n\tint y = r;\r\n\r\n\tstd::vector<cv::Point2i> line;\r\n\r\n\tline = bresLine(x0, y0 - r, x0, y0 + r);\r\n\tfor (std::vector<cv::Point2i>::iterator it = line.begin(); it != line.end(); ++it)\r\n\t{\r\n\t\tmask[it->x - x0 + r][it->y - y0 + r] = true;\r\n\t}\r\n\r\n\tline = bresLine(x0 - r, y0, x0 + r, y0);\r\n\tfor (std::vector<cv::Point2i>::iterator it = line.begin(); it != line.end(); ++it)\r\n\t{\r\n\t\tmask[it->x - x0 + r][it->y - y0 + r] = true;\r\n\t}\r\n\r\n\twhile (x < y)\r\n\t{\r\n\t\tif (f >= 0)\r\n\t\t{\r\n\t\t\ty--;\r\n\t\t\tddF_y += 2;\r\n\t\t\tf += ddF_y;\r\n\t\t}\r\n\r\n\t\tx++;\r\n\t\tddF_x += 2;\r\n\t\tf += ddF_x;\r\n\r\n\t\tline = bresLine(x0 - x, y0 + y, x0 + x, y0 + y);\r\n\t\tfor (std::vector<cv::Point2i>::iterator it = line.begin(); it != line.end(); ++it)\r\n\t\t{\r\n\t\t\tmask[it->x - x0 + r][it->y - y0 + r] = true;\r\n\t\t}\r\n\r\n\t\tline = bresLine(x0 - x, y0 - y, x0 + x, y0 - y);\r\n\t\tfor (std::vector<cv::Point2i>::iterator it = line.begin(); it != line.end(); ++it)\r\n\t\t{\r\n\t\t\tmask[it->x - x0 + r][it->y - y0 + r] = true;\r\n\t\t}\r\n\r\n\t\tline = bresLine(x0 - y, y0 + x, x0 + y, y0 + x);\r\n\t\tfor (std::vector<cv::Point2i>::iterator it = line.begin(); it != line.end(); ++it)\r\n\t\t{\r\n\t\t\tmask[it->x - x0 + r][it->y - y0 + r] = true;\r\n\t\t}\r\n\r\n\t\tline = bresLine(x0 - y, y0 - x, x0 + y, y0 - x);\r\n\t\tfor (std::vector<cv::Point2i>::iterator it = line.begin(); it != line.end(); ++it)\r\n\t\t{\r\n\t\t\tmask[it->x - x0 + r][it->y - y0 + r] = true;\r\n\t\t}\r\n\t}\r\n\r\n\tstd::vector<cv::Point2i> cells;\r\n\tfor (int i = 0; i < 2 * r + 1; ++i)\r\n\t{\r\n\t\tfor (int j = 0; j < 2 * r + 1; ++j)\r\n\t\t{\r\n\t\t\tif (mask[i][j])\r\n\t\t\t{\r\n\t\t\t\tcells.push_back(cv::Point2i(i - r + x0, j - r + y0));\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\treturn cells;\r\n}\r\n\r\nvoid\r\nfitCircle(const std::vector<cv::Point2d>& points,\r\n          double& centerX, double& centerY, double& radius)\r\n{\r\n    // D. Umbach, and K. Jones, A Few Methods for Fitting Circles to Data,\r\n    // IEEE Transactions on Instrumentation and Measurement, 2000\r\n    // We use the modified least squares method.\r\n    double sum_x = 0.0;\r\n    double sum_y = 0.0;\r\n    double sum_xx = 0.0;\r\n    double sum_xy = 0.0;\r\n    double sum_yy = 0.0;\r\n    double sum_xxx = 0.0;\r\n    double sum_xxy = 0.0;\r\n    double sum_xyy = 0.0;\r\n    double sum_yyy = 0.0;\r\n\r\n    int n = points.size();\r\n    for (int i = 0; i < n; ++i)\r\n    {\r\n        double x = points.at(i).x;\r\n        double y = points.at(i).y;\r\n\r\n        sum_x += x;\r\n        sum_y += y;\r\n        sum_xx += x * x;\r\n        sum_xy += x * y;\r\n        sum_yy += y * y;\r\n        sum_xxx += x * x * x;\r\n        sum_xxy += x * x * y;\r\n        sum_xyy += x * y * y;\r\n        sum_yyy += y * y * y;\r\n    }\r\n\r\n    double A = n * sum_xx - square(sum_x);\r\n    double B = n * sum_xy - sum_x * sum_y;\r\n    double C = n * sum_yy - square(sum_y);\r\n    double D = 0.5 * (n * sum_xyy - sum_x * sum_yy + n * sum_xxx - sum_x * sum_xx);\r\n    double E = 0.5 * (n * sum_xxy - sum_y * sum_xx + n * sum_yyy - sum_y * sum_yy);\r\n\r\n    centerX = (D * C - B * E) / (A * C - square(B));\r\n    centerY = (A * E - B * D) / (A * C - square(B));\r\n\r\n    double sum_r = 0.0;\r\n    for (int i = 0; i < n; ++i)\r\n    {\r\n        double x = points.at(i).x;\r\n        double y = points.at(i).y;\r\n\r\n        sum_r += hypot(x - centerX, y - centerY);\r\n    }\r\n\r\n    radius = sum_r / n;\r\n}\r\n\r\nstd::vector<cv::Point2d>\r\nintersectCircles(double x1, double y1, double r1,\r\n                 double x2, double y2, double r2)\r\n{\r\n    std::vector<cv::Point2d> ipts;\r\n\r\n    double d = hypot(x1 - x2, y1 - y2);\r\n    if (d > r1 + r2)\r\n    {\r\n        // circles are separate\r\n        return ipts;\r\n    }\r\n    if (d < fabs(r1 - r2))\r\n    {\r\n        // one circle is contained within the other\r\n        return ipts;\r\n    }\r\n\r\n    double a = (square(r1) - square(r2) + square(d)) / (2.0 * d);\r\n    double h = sqrt(square(r1) - square(a));\r\n\r\n    double x3 = x1 + a * (x2 - x1) / d;\r\n    double y3 = y1 + a * (y2 - y1) / d;\r\n\r\n    if (h < 1e-10)\r\n    {\r\n        // two circles touch at one point\r\n        ipts.push_back(cv::Point2d(x3, y3));\r\n        return ipts;\r\n    }\r\n\r\n    ipts.push_back(cv::Point2d(x3 + h * (y2 - y1) / d,\r\n                               y3 - h * (x2 - x1) / d));\r\n    ipts.push_back(cv::Point2d(x3 - h * (y2 - y1) / d,\r\n                               y3 + h * (x2 - x1) / d));\r\n    return ipts;\r\n}\r\n\r\nchar\r\nUTMLetterDesignator(double latitude)\r\n{\r\n    // This routine determines the correct UTM letter designator for the given latitude\r\n    // returns 'Z' if latitude is outside the UTM limits of 84N to 80S\r\n    // Written by Chuck Gantz- chuck.gantz@globalstar.com\r\n    char letterDesignator;\r\n\r\n    if ((84.0 >= latitude) && (latitude >= 72.0)) letterDesignator = 'X';\r\n    else if ((72.0 > latitude) && (latitude >= 64.0)) letterDesignator = 'W';\r\n    else if ((64.0 > latitude) && (latitude >= 56.0)) letterDesignator = 'V';\r\n    else if ((56.0 > latitude) && (latitude >= 48.0)) letterDesignator = 'U';\r\n    else if ((48.0 > latitude) && (latitude >= 40.0)) letterDesignator = 'T';\r\n    else if ((40.0 > latitude) && (latitude >= 32.0)) letterDesignator = 'S';\r\n    else if ((32.0 > latitude) && (latitude >= 24.0)) letterDesignator = 'R';\r\n    else if ((24.0 > latitude) && (latitude >= 16.0)) letterDesignator = 'Q';\r\n    else if ((16.0 > latitude) && (latitude >= 8.0)) letterDesignator = 'P';\r\n    else if (( 8.0 > latitude) && (latitude >= 0.0)) letterDesignator = 'N';\r\n    else if (( 0.0 > latitude) && (latitude >= -8.0)) letterDesignator = 'M';\r\n    else if ((-8.0 > latitude) && (latitude >= -16.0)) letterDesignator = 'L';\r\n    else if ((-16.0 > latitude) && (latitude >= -24.0)) letterDesignator = 'K';\r\n    else if ((-24.0 > latitude) && (latitude >= -32.0)) letterDesignator = 'J';\r\n    else if ((-32.0 > latitude) && (latitude >= -40.0)) letterDesignator = 'H';\r\n    else if ((-40.0 > latitude) && (latitude >= -48.0)) letterDesignator = 'G';\r\n    else if ((-48.0 > latitude) && (latitude >= -56.0)) letterDesignator = 'F';\r\n    else if ((-56.0 > latitude) && (latitude >= -64.0)) letterDesignator = 'E';\r\n    else if ((-64.0 > latitude) && (latitude >= -72.0)) letterDesignator = 'D';\r\n    else if ((-72.0 > latitude) && (latitude >= -80.0)) letterDesignator = 'C';\r\n    else letterDesignator = 'Z'; //This is here as an error flag to show that the Latitude is outside the UTM limits\r\n\r\n    return letterDesignator;\r\n}\r\n\r\nvoid\r\nLLtoUTM(double latitude, double longitude,\r\n        double& utmNorthing, double& utmEasting, std::string& utmZone)\r\n{\r\n    // converts lat/long to UTM coords.  Equations from USGS Bulletin 1532\r\n    // East Longitudes are positive, West longitudes are negative.\r\n    // North latitudes are positive, South latitudes are negative\r\n    // Lat and Long are in decimal degrees\r\n    // Written by Chuck Gantz- chuck.gantz@globalstar.com\r\n\r\n    double k0 = 0.9996;\r\n\r\n    double LongOrigin;\r\n    double eccPrimeSquared;\r\n    double N, T, C, A, M;\r\n\r\n    double LatRad = latitude * M_PI / 180.0;\r\n    double LongRad = longitude * M_PI / 180.0;\r\n    double LongOriginRad;\r\n\r\n    int ZoneNumber = static_cast<int>((longitude + 180.0) / 6.0) + 1;\r\n\r\n    if (latitude >= 56.0 && latitude < 64.0 &&\r\n            longitude >= 3.0 && longitude < 12.0) {\r\n        ZoneNumber = 32;\r\n    }\r\n\r\n    // Special zones for Svalbard\r\n    if (latitude >= 72.0 && latitude < 84.0) {\r\n        if (     longitude >= 0.0  && longitude <  9.0) ZoneNumber = 31;\r\n        else if (longitude >= 9.0  && longitude < 21.0) ZoneNumber = 33;\r\n        else if (longitude >= 21.0 && longitude < 33.0) ZoneNumber = 35;\r\n        else if (longitude >= 33.0 && longitude < 42.0) ZoneNumber = 37;\r\n    }\r\n    LongOrigin = static_cast<double>((ZoneNumber - 1) * 6 - 180 + 3);  //+3 puts origin in middle of zone\r\n    LongOriginRad = LongOrigin * M_PI / 180.0;\r\n\r\n    // compute the UTM Zone from the latitude and longitude\r\n    std::ostringstream oss;\r\n    oss << ZoneNumber << UTMLetterDesignator(latitude);\r\n    utmZone = oss.str();\r\n\r\n    eccPrimeSquared = WGS84_ECCSQ / (1.0 - WGS84_ECCSQ);\r\n\r\n    N = WGS84_A / sqrt(1.0 - WGS84_ECCSQ * sin(LatRad) * sin(LatRad));\r\n    T = tan(LatRad) * tan(LatRad);\r\n    C = eccPrimeSquared * cos(LatRad) * cos(LatRad);\r\n    A = cos(LatRad) * (LongRad - LongOriginRad);\r\n\r\n    M = WGS84_A * ((1.0 - WGS84_ECCSQ / 4.0\r\n                    - 3.0 * WGS84_ECCSQ * WGS84_ECCSQ / 64.0\r\n                    - 5.0 * WGS84_ECCSQ * WGS84_ECCSQ * WGS84_ECCSQ / 256.0)\r\n                   * LatRad\r\n                   - (3.0 * WGS84_ECCSQ / 8.0\r\n                      + 3.0 * WGS84_ECCSQ * WGS84_ECCSQ / 32.0\r\n                      + 45.0 * WGS84_ECCSQ * WGS84_ECCSQ * WGS84_ECCSQ / 1024.0)\r\n                   * sin(2.0 * LatRad)\r\n                   + (15.0 * WGS84_ECCSQ * WGS84_ECCSQ / 256.0\r\n                      + 45.0 * WGS84_ECCSQ * WGS84_ECCSQ * WGS84_ECCSQ / 1024.0)\r\n                   * sin(4.0 * LatRad)\r\n                   - (35.0 * WGS84_ECCSQ * WGS84_ECCSQ * WGS84_ECCSQ / 3072.0)\r\n                   * sin(6.0 * LatRad));\r\n\r\n    utmEasting = k0 * N * (A + (1.0 - T + C) * A * A * A / 6.0\r\n                           + (5.0 - 18.0 * T + T * T + 72.0 * C\r\n                              - 58.0 * eccPrimeSquared)\r\n                           * A * A * A * A * A / 120.0)\r\n                 + 500000.0;\r\n\r\n    utmNorthing = k0 * (M + N * tan(LatRad) *\r\n                        (A * A / 2.0 +\r\n                         (5.0 - T + 9.0 * C + 4.0 * C * C) * A * A * A * A / 24.0\r\n                         + (61.0 - 58.0 * T + T * T + 600.0 * C\r\n                            - 330.0 * eccPrimeSquared)\r\n                         * A * A * A * A * A * A / 720.0));\r\n    if (latitude < 0.0) {\r\n        utmNorthing += 10000000.0; //10000000 meter offset for southern hemisphere\r\n    }\r\n}\r\n\r\nvoid\r\nUTMtoLL(double utmNorthing, double utmEasting, const std::string& utmZone,\r\n        double& latitude, double& longitude)\r\n{\r\n    // converts UTM coords to lat/long.  Equations from USGS Bulletin 1532\r\n    // East Longitudes are positive, West longitudes are negative.\r\n    // North latitudes are positive, South latitudes are negative\r\n    // Lat and Long are in decimal degrees.\r\n    // Written by Chuck Gantz- chuck.gantz@globalstar.com\r\n\r\n    double k0 = 0.9996;\r\n    double eccPrimeSquared;\r\n    double e1 = (1.0 - sqrt(1.0 - WGS84_ECCSQ)) / (1.0 + sqrt(1.0 - WGS84_ECCSQ));\r\n    double N1, T1, C1, R1, D, M;\r\n    double LongOrigin;\r\n    double mu, phi1, phi1Rad;\r\n    double x, y;\r\n    int ZoneNumber;\r\n    char ZoneLetter;\r\n    bool NorthernHemisphere;\r\n\r\n    x = utmEasting - 500000.0; //remove 500,000 meter offset for longitude\r\n    y = utmNorthing;\r\n\r\n    std::istringstream iss(utmZone);\r\n    iss >> ZoneNumber >> ZoneLetter;\r\n    if ((static_cast<int>(ZoneLetter) - static_cast<int>('N')) >= 0) {\r\n        NorthernHemisphere = true;//point is in northern hemisphere\r\n    } else {\r\n        NorthernHemisphere = false;//point is in southern hemisphere\r\n        y -= 10000000.0;//remove 10,000,000 meter offset used for southern hemisphere\r\n    }\r\n\r\n    LongOrigin = (ZoneNumber - 1.0) * 6.0 - 180.0 + 3.0;  //+3 puts origin in middle of zone\r\n\r\n    eccPrimeSquared = WGS84_ECCSQ / (1.0 - WGS84_ECCSQ);\r\n\r\n    M = y / k0;\r\n    mu = M / (WGS84_A * (1.0 - WGS84_ECCSQ / 4.0\r\n                         - 3.0 * WGS84_ECCSQ * WGS84_ECCSQ / 64.0\r\n                         - 5.0 * WGS84_ECCSQ * WGS84_ECCSQ * WGS84_ECCSQ / 256.0));\r\n\r\n    phi1Rad = mu + (3.0 * e1 / 2.0 - 27.0 * e1 * e1 * e1 / 32.0) * sin(2.0 * mu)\r\n              + (21.0 * e1 * e1 / 16.0 - 55.0 * e1 * e1 * e1 * e1 / 32.0)\r\n              * sin(4.0 * mu)\r\n              + (151.0 * e1 * e1 * e1 / 96.0) * sin(6.0 * mu);\r\n    phi1 = phi1Rad / M_PI * 180.0;\r\n\r\n    N1 = WGS84_A / sqrt(1.0 - WGS84_ECCSQ * sin(phi1Rad) * sin(phi1Rad));\r\n    T1 = tan(phi1Rad) * tan(phi1Rad);\r\n    C1 = eccPrimeSquared * cos(phi1Rad) * cos(phi1Rad);\r\n    R1 = WGS84_A * (1.0 - WGS84_ECCSQ) /\r\n         pow(1.0 - WGS84_ECCSQ * sin(phi1Rad) * sin(phi1Rad), 1.5);\r\n    D = x / (N1 * k0);\r\n\r\n    latitude = phi1Rad - (N1 * tan(phi1Rad) / R1)\r\n               * (D * D / 2.0 - (5.0 + 3.0 * T1 + 10.0 * C1 - 4.0 * C1 * C1\r\n                                 - 9.0 * eccPrimeSquared) * D * D * D * D / 24.0\r\n                  + (61.0 + 90.0 * T1 + 298.0 * C1 + 45.0 * T1 * T1\r\n                     - 252.0 * eccPrimeSquared - 3.0 * C1 * C1)\r\n                  * D * D * D * D * D * D / 720.0);\r\n    latitude *= 180.0 / M_PI;\r\n\r\n    longitude = (D - (1.0 + 2.0 * T1 + C1) * D * D * D / 6.0\r\n                 + (5.0 - 2.0 * C1 + 28.0 * T1 - 3.0 * C1 * C1\r\n                    + 8.0 * eccPrimeSquared + 24.0 * T1 * T1)\r\n                 * D * D * D * D * D / 120.0) / cos(phi1Rad);\r\n    longitude = LongOrigin + longitude / M_PI * 180.0;\r\n}\r\n\r\nlong int\r\ntimestampDiff(uint64_t t1, uint64_t t2)\r\n{\r\n    if (t2 > t1)\r\n    {\r\n        uint64_t d = t2 - t1;\r\n\r\n        if (d > std::numeric_limits<long int>::max())\r\n        {\r\n            return std::numeric_limits<long int>::max();\r\n        }\r\n        else\r\n        {\r\n            return d;\r\n        }\r\n    }\r\n    else\r\n    {\r\n        uint64_t d = t1 - t2;\r\n\r\n        if (d > std::numeric_limits<long int>::max())\r\n        {\r\n            return std::numeric_limits<long int>::min();\r\n        }\r\n        else\r\n        {\r\n            return - static_cast<long int>(d);\r\n        }\r\n    }\r\n}\r\n\r\n}\r\n"
  },
  {
    "path": "src/utils/camodocal/src/intrinsic_calib.cc",
    "content": "#include <boost/algorithm/string.hpp>\n#include <boost/filesystem.hpp>\n#include <boost/program_options.hpp>\n#include <iomanip>\n#include <iostream>\n#include <algorithm>\n#include <opencv2/core/core.hpp>\n#include <opencv2/imgproc/imgproc.hpp>\n#include <opencv2/highgui/highgui.hpp>\n\n#include \"camodocal/chessboard/Chessboard.h\"\n#include \"camodocal/calib/CameraCalibration.h\"\n#include \"camodocal/gpl/gpl.h\"\n\nint main(int argc, char** argv)\n{\n    cv::Size boardSize;\n    float squareSize;\n    std::string inputDir;\n    std::string cameraModel;\n    std::string cameraName;\n    std::string prefix;\n    std::string fileExtension;\n    bool useOpenCV;\n    bool viewResults;\n    bool verbose;\n\n    //========= Handling Program options =========\n    boost::program_options::options_description desc(\"Allowed options\");\n    desc.add_options()\n        (\"help\", \"produce help message\")\n        (\"width,w\", boost::program_options::value<int>(&boardSize.width)->default_value(8), \"Number of inner corners on the chessboard pattern in x direction\")\n        (\"height,h\", boost::program_options::value<int>(&boardSize.height)->default_value(12), \"Number of inner corners on the chessboard pattern in y direction\")\n        (\"size,s\", boost::program_options::value<float>(&squareSize)->default_value(7.f), \"Size of one square in mm\")\n        (\"input,i\", boost::program_options::value<std::string>(&inputDir)->default_value(\"calibrationdata\"), \"Input directory containing chessboard images\")\n        (\"prefix,p\", boost::program_options::value<std::string>(&prefix)->default_value(\"left-\"), \"Prefix of images\")\n        (\"file-extension,e\", boost::program_options::value<std::string>(&fileExtension)->default_value(\".png\"), \"File extension of images\")\n        (\"camera-model\", boost::program_options::value<std::string>(&cameraModel)->default_value(\"mei\"), \"Camera model: kannala-brandt | mei | pinhole\")\n        (\"camera-name\", boost::program_options::value<std::string>(&cameraName)->default_value(\"camera\"), \"Name of camera\")\n        (\"opencv\", boost::program_options::bool_switch(&useOpenCV)->default_value(true), \"Use OpenCV to detect corners\")\n        (\"view-results\", boost::program_options::bool_switch(&viewResults)->default_value(false), \"View results\")\n        (\"verbose,v\", boost::program_options::bool_switch(&verbose)->default_value(true), \"Verbose output\")\n        ;\n\n    boost::program_options::positional_options_description pdesc;\n    pdesc.add(\"input\", 1);\n\n    boost::program_options::variables_map vm;\n    boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(desc).positional(pdesc).run(), vm);\n    boost::program_options::notify(vm);\n\n    if (vm.count(\"help\"))\n    {\n        std::cout << desc << std::endl;\n        return 1;\n    }\n\n    if (!boost::filesystem::exists(inputDir) && !boost::filesystem::is_directory(inputDir))\n    {\n        std::cerr << \"# ERROR: Cannot find input directory \" << inputDir << \".\" << std::endl;\n        return 1;\n    }\n\n    camodocal::Camera::ModelType modelType;\n    if (boost::iequals(cameraModel, \"kannala-brandt\"))\n    {\n        modelType = camodocal::Camera::KANNALA_BRANDT;\n    }\n    else if (boost::iequals(cameraModel, \"mei\"))\n    {\n        modelType = camodocal::Camera::MEI;\n    }\n    else if (boost::iequals(cameraModel, \"pinhole\"))\n    {\n        modelType = camodocal::Camera::PINHOLE;\n    }\n    else if (boost::iequals(cameraModel, \"scaramuzza\"))\n    {\n        modelType = camodocal::Camera::SCARAMUZZA;\n    }\n    else\n    {\n        std::cerr << \"# ERROR: Unknown camera model: \" << cameraModel << std::endl;\n        return 1;\n    }\n\n    switch (modelType)\n    {\n    case camodocal::Camera::KANNALA_BRANDT:\n        std::cout << \"# INFO: Camera model: Kannala-Brandt\" << std::endl;\n        break;\n    case camodocal::Camera::MEI:\n        std::cout << \"# INFO: Camera model: Mei\" << std::endl;\n        break;\n    case camodocal::Camera::PINHOLE:\n        std::cout << \"# INFO: Camera model: Pinhole\" << std::endl;\n        break;\n    case camodocal::Camera::SCARAMUZZA:\n        std::cout << \"# INFO: Camera model: Scaramuzza-Omnidirect\" << std::endl;\n        break;\n    }\n\n    // look for images in input directory\n    std::vector<std::string> imageFilenames;\n    boost::filesystem::directory_iterator itr;\n    for (boost::filesystem::directory_iterator itr(inputDir); itr != boost::filesystem::directory_iterator(); ++itr)\n    {\n        if (!boost::filesystem::is_regular_file(itr->status()))\n        {\n            continue;\n        }\n\n        std::string filename = itr->path().filename().string();\n\n        // check if prefix matches\n        if (!prefix.empty())\n        {\n            if (filename.compare(0, prefix.length(), prefix) != 0)\n            {\n                continue;\n            }\n        }\n\n        // check if file extension matches\n        if (filename.compare(filename.length() - fileExtension.length(), fileExtension.length(), fileExtension) != 0)\n        {\n            continue;\n        }\n\n        imageFilenames.push_back(itr->path().string());\n\n        if (verbose)\n        {\n            std::cerr << \"# INFO: Adding \" << imageFilenames.back() << std::endl;\n        }\n    }\n\n    if (imageFilenames.empty())\n    {\n        std::cerr << \"# ERROR: No chessboard images found.\" << std::endl;\n        return 1;\n    }\n\n    if (verbose)\n    {\n        std::cerr << \"# INFO: # images: \" << imageFilenames.size() << std::endl;\n    }\n\n    std::sort(imageFilenames.begin(), imageFilenames.end());\n\n    cv::Mat image = cv::imread(imageFilenames.front(), -1);\n    const cv::Size frameSize = image.size();\n\n    camodocal::CameraCalibration calibration(modelType, cameraName, frameSize, boardSize, squareSize);\n    calibration.setVerbose(verbose);\n\n    std::vector<bool> chessboardFound(imageFilenames.size(), false);\n    for (size_t i = 0; i < imageFilenames.size(); ++i)\n    {\n        // image = cv::imread(imageFilenames.at(i), -1);\n        image = cv::imread(imageFilenames.at(i), 0);\n\n        camodocal::Chessboard chessboard(boardSize, image);\n\n        chessboard.findCorners(useOpenCV);\n        if (chessboard.cornersFound())\n        {\n            if (verbose)\n            {\n                std::cerr << \"# INFO: Detected chessboard in image \" << i + 1 << \", \" << imageFilenames.at(i) << std::endl;\n            }\n\n            calibration.addChessboardData(chessboard.getCorners());\n\n            cv::Mat sketch;\n            chessboard.getSketch().copyTo(sketch);\n\n            cv::imshow(\"Image\", sketch);\n            cv::waitKey(50);\n        }\n        else if (verbose)\n        {\n            std::cerr << \"# INFO: Did not detect chessboard in image \" << i + 1 << std::endl;\n        }\n        chessboardFound.at(i) = chessboard.cornersFound();\n    }\n    cv::destroyWindow(\"Image\");\n\n    if (calibration.sampleCount() < 10)\n    {\n        std::cerr << \"# ERROR: Insufficient number of detected chessboards.\" << std::endl;\n        return 1;\n    }\n\n    if (verbose)\n    {\n        std::cerr << \"# INFO: Calibrating...\" << std::endl;\n    }\n\n    double startTime = camodocal::timeInSeconds();\n\n    calibration.calibrate();\n    calibration.writeParams(cameraName + \"_camera_calib.yaml\");\n    calibration.writeChessboardData(cameraName + \"_chessboard_data.dat\");\n\n    if (verbose)\n    {\n        std::cout << \"# INFO: Calibration took a total time of \"\n                  << std::fixed << std::setprecision(3) << camodocal::timeInSeconds() - startTime\n                  << \" sec.\\n\";\n    }\n\n    if (verbose)\n    {\n        std::cerr << \"# INFO: Wrote calibration file to \" << cameraName + \"_camera_calib.yaml\" << std::endl;\n    }\n\n    if (viewResults)\n    {\n        std::vector<cv::Mat> cbImages;\n        std::vector<std::string> cbImageFilenames;\n\n        for (size_t i = 0; i < imageFilenames.size(); ++i)\n        {\n            if (!chessboardFound.at(i))\n            {\n                continue;\n            }\n\n            cbImages.push_back(cv::imread(imageFilenames.at(i), -1));\n            cbImageFilenames.push_back(imageFilenames.at(i));\n        }\n\n        // visualize observed and reprojected points\n        calibration.drawResults(cbImages);\n\n        for (size_t i = 0; i < cbImages.size(); ++i)\n        {\n            cv::putText(cbImages.at(i), cbImageFilenames.at(i), cv::Point(10,20),\n                        cv::FONT_HERSHEY_COMPLEX, 0.5, cv::Scalar(255, 255, 255),\n                        1, CV_AA);\n            cv::imshow(\"Image\", cbImages.at(i));\n            cv::waitKey(0);\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "src/utils/camodocal/src/sparse_graph/Transform.cc",
    "content": "#include <camodocal/sparse_graph/Transform.h>\n\nnamespace camodocal\n{\n\nTransform::Transform()\n{\n    m_q.setIdentity();\n    m_t.setZero();\n}\n\nTransform::Transform(const Eigen::Matrix4d& H)\n{\n   m_q = Eigen::Quaterniond(H.block<3,3>(0,0));\n   m_t = H.block<3,1>(0,3);\n}\n\nEigen::Quaterniond&\nTransform::rotation(void)\n{\n    return m_q;\n}\n\nconst Eigen::Quaterniond&\nTransform::rotation(void) const\n{\n    return m_q;\n}\n\ndouble*\nTransform::rotationData(void)\n{\n    return m_q.coeffs().data();\n}\n\nconst double* const\nTransform::rotationData(void) const\n{\n    return m_q.coeffs().data();\n}\n\nEigen::Vector3d&\nTransform::translation(void)\n{\n    return m_t;\n}\n\nconst Eigen::Vector3d&\nTransform::translation(void) const\n{\n    return m_t;\n}\n\ndouble*\nTransform::translationData(void)\n{\n    return m_t.data();\n}\n\nconst double* const\nTransform::translationData(void) const\n{\n    return m_t.data();\n}\n\nEigen::Matrix4d\nTransform::toMatrix(void) const\n{\n    Eigen::Matrix4d H;\n    H.setIdentity();\n    H.block<3,3>(0,0) = m_q.toRotationMatrix();\n    H.block<3,1>(0,3) = m_t;\n\n    return H;\n}\n\n}\n"
  },
  {
    "path": "src/utils/nlohmann/json.hpp",
    "content": "/*\n    __ _____ _____ _____\n __|  |   __|     |   | |  JSON for Modern C++\n|  |  |__   |  |  | | | |  version 3.4.0\n|_____|_____|_____|_|___|  https://github.com/nlohmann/json\n\nLicensed under the MIT License <http://opensource.org/licenses/MIT>.\nSPDX-License-Identifier: MIT\nCopyright (c) 2013-2018 Niels Lohmann <http://nlohmann.me>.\n\nPermission is hereby  granted, free of charge, to any  person obtaining a copy\nof this software and associated  documentation files (the \"Software\"), to deal\nin the Software  without restriction, including without  limitation the rights\nto  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell\ncopies  of  the Software,  and  to  permit persons  to  whom  the Software  is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE  IS PROVIDED \"AS  IS\", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR\nIMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,\nFITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE\nAUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER\nLIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n#ifndef NLOHMANN_JSON_HPP\n#define NLOHMANN_JSON_HPP\n\n#define NLOHMANN_JSON_VERSION_MAJOR 3\n#define NLOHMANN_JSON_VERSION_MINOR 4\n#define NLOHMANN_JSON_VERSION_PATCH 0\n\n#include <algorithm> // all_of, find, for_each\n#include <cassert> // assert\n#include <ciso646> // and, not, or\n#include <cstddef> // nullptr_t, ptrdiff_t, size_t\n#include <functional> // hash, less\n#include <initializer_list> // initializer_list\n#include <iosfwd> // istream, ostream\n#include <iterator> // iterator_traits, random_access_iterator_tag\n#include <numeric> // accumulate\n#include <string> // string, stoi, to_string\n#include <utility> // declval, forward, move, pair, swap\n\n// #include <nlohmann/json_fwd.hpp>\n#ifndef NLOHMANN_JSON_FWD_HPP\n#define NLOHMANN_JSON_FWD_HPP\n\n#include <cstdint> // int64_t, uint64_t\n#include <map> // map\n#include <memory> // allocator\n#include <string> // string\n#include <vector> // vector\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nnamespace nlohmann\n{\n/*!\n@brief default JSONSerializer template argument\n\nThis serializer ignores the template arguments and uses ADL\n([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))\nfor serialization.\n*/\ntemplate<typename T = void, typename SFINAE = void>\nstruct adl_serializer;\n\ntemplate<template<typename U, typename V, typename... Args> class ObjectType =\n         std::map,\n         template<typename U, typename... Args> class ArrayType = std::vector,\n         class StringType = std::string, class BooleanType = bool,\n         class NumberIntegerType = std::int64_t,\n         class NumberUnsignedType = std::uint64_t,\n         class NumberFloatType = double,\n         template<typename U> class AllocatorType = std::allocator,\n         template<typename T, typename SFINAE = void> class JSONSerializer =\n         adl_serializer>\nclass basic_json;\n\n/*!\n@brief JSON Pointer\n\nA JSON pointer defines a string syntax for identifying a specific value\nwithin a JSON document. It can be used with functions `at` and\n`operator[]`. Furthermore, JSON pointers are the base for JSON patches.\n\n@sa [RFC 6901](https://tools.ietf.org/html/rfc6901)\n\n@since version 2.0.0\n*/\ntemplate<typename BasicJsonType>\nclass json_pointer;\n\n/*!\n@brief default JSON class\n\nThis type is the default specialization of the @ref basic_json class which\nuses the standard template types.\n\n@since version 1.0.0\n*/\nusing json = basic_json<>;\n}  // namespace nlohmann\n\n#endif\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\n// This file contains all internal macro definitions\n// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them\n\n// exclude unsupported compilers\n#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK)\n    #if defined(__clang__)\n        #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400\n            #error \"unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers\"\n        #endif\n    #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER))\n        #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800\n            #error \"unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers\"\n        #endif\n    #endif\n#endif\n\n// disable float-equal warnings on GCC/clang\n#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#endif\n\n// disable documentation warnings on clang\n#if defined(__clang__)\n    #pragma GCC diagnostic push\n    #pragma GCC diagnostic ignored \"-Wdocumentation\"\n#endif\n\n// allow for portable deprecation warnings\n#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)\n    #define JSON_DEPRECATED __attribute__((deprecated))\n#elif defined(_MSC_VER)\n    #define JSON_DEPRECATED __declspec(deprecated)\n#else\n    #define JSON_DEPRECATED\n#endif\n\n// allow to disable exceptions\n#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION)\n    #define JSON_THROW(exception) throw exception\n    #define JSON_TRY try\n    #define JSON_CATCH(exception) catch(exception)\n    #define JSON_INTERNAL_CATCH(exception) catch(exception)\n#else\n    #define JSON_THROW(exception) std::abort()\n    #define JSON_TRY if(true)\n    #define JSON_CATCH(exception) if(false)\n    #define JSON_INTERNAL_CATCH(exception) if(false)\n#endif\n\n// override exception macros\n#if defined(JSON_THROW_USER)\n    #undef JSON_THROW\n    #define JSON_THROW JSON_THROW_USER\n#endif\n#if defined(JSON_TRY_USER)\n    #undef JSON_TRY\n    #define JSON_TRY JSON_TRY_USER\n#endif\n#if defined(JSON_CATCH_USER)\n    #undef JSON_CATCH\n    #define JSON_CATCH JSON_CATCH_USER\n    #undef JSON_INTERNAL_CATCH\n    #define JSON_INTERNAL_CATCH JSON_CATCH_USER\n#endif\n#if defined(JSON_INTERNAL_CATCH_USER)\n    #undef JSON_INTERNAL_CATCH\n    #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER\n#endif\n\n// manual branch prediction\n#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)\n    #define JSON_LIKELY(x)      __builtin_expect(!!(x), 1)\n    #define JSON_UNLIKELY(x)    __builtin_expect(!!(x), 0)\n#else\n    #define JSON_LIKELY(x)      x\n    #define JSON_UNLIKELY(x)    x\n#endif\n\n// C++ language standard detection\n#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464\n    #define JSON_HAS_CPP_17\n    #define JSON_HAS_CPP_14\n#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)\n    #define JSON_HAS_CPP_14\n#endif\n\n/*!\n@brief macro to briefly define a mapping between an enum and JSON\n@def NLOHMANN_JSON_SERIALIZE_ENUM\n@since version 3.4.0\n*/\n#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...)                                           \\\n    template<typename BasicJsonType>                                                           \\\n    inline void to_json(BasicJsonType& j, const ENUM_TYPE& e)                                  \\\n    {                                                                                          \\\n        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE \" must be an enum!\");         \\\n        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                    \\\n        auto it = std::find_if(std::begin(m), std::end(m),                                     \\\n                               [e](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \\\n        {                                                                                      \\\n            return ej_pair.first == e;                                                         \\\n        });                                                                                    \\\n        j = ((it != std::end(m)) ? it : std::begin(m))->second;                                \\\n    }                                                                                          \\\n    template<typename BasicJsonType>                                                           \\\n    inline void from_json(const BasicJsonType& j, ENUM_TYPE& e)                                \\\n    {                                                                                          \\\n        static_assert(std::is_enum<ENUM_TYPE>::value, #ENUM_TYPE \" must be an enum!\");         \\\n        static const std::pair<ENUM_TYPE, BasicJsonType> m[] = __VA_ARGS__;                    \\\n        auto it = std::find_if(std::begin(m), std::end(m),                                     \\\n                               [j](const std::pair<ENUM_TYPE, BasicJsonType>& ej_pair) -> bool \\\n        {                                                                                      \\\n            return ej_pair.second == j;                                                        \\\n        });                                                                                    \\\n        e = ((it != std::end(m)) ? it : std::begin(m))->first;                                 \\\n    }\n\n// Ugly macros to avoid uglier copy-paste when specializing basic_json. They\n// may be removed in the future once the class is split.\n\n#define NLOHMANN_BASIC_JSON_TPL_DECLARATION                                \\\n    template<template<typename, typename, typename...> class ObjectType,   \\\n             template<typename, typename...> class ArrayType,              \\\n             class StringType, class BooleanType, class NumberIntegerType, \\\n             class NumberUnsignedType, class NumberFloatType,              \\\n             template<typename> class AllocatorType,                       \\\n             template<typename, typename = void> class JSONSerializer>\n\n#define NLOHMANN_BASIC_JSON_TPL                                            \\\n    basic_json<ObjectType, ArrayType, StringType, BooleanType,             \\\n    NumberIntegerType, NumberUnsignedType, NumberFloatType,                \\\n    AllocatorType, JSONSerializer>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n\n#include <ciso646> // not\n#include <cstddef> // size_t\n#include <type_traits> // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type\n\nnamespace nlohmann\n{\nnamespace detail\n{\n// alias templates to reduce boilerplate\ntemplate<bool B, typename T = void>\nusing enable_if_t = typename std::enable_if<B, T>::type;\n\ntemplate<typename T>\nusing uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;\n\n// implementation of C++14 index_sequence and affiliates\n// source: https://stackoverflow.com/a/32223343\ntemplate<std::size_t... Ints>\nstruct index_sequence\n{\n    using type = index_sequence;\n    using value_type = std::size_t;\n    static constexpr std::size_t size() noexcept\n    {\n        return sizeof...(Ints);\n    }\n};\n\ntemplate<class Sequence1, class Sequence2>\nstruct merge_and_renumber;\n\ntemplate<std::size_t... I1, std::size_t... I2>\nstruct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>\n        : index_sequence < I1..., (sizeof...(I1) + I2)... > {};\n\ntemplate<std::size_t N>\nstruct make_index_sequence\n    : merge_and_renumber < typename make_index_sequence < N / 2 >::type,\n      typename make_index_sequence < N - N / 2 >::type > {};\n\ntemplate<> struct make_index_sequence<0> : index_sequence<> {};\ntemplate<> struct make_index_sequence<1> : index_sequence<0> {};\n\ntemplate<typename... Ts>\nusing index_sequence_for = make_index_sequence<sizeof...(Ts)>;\n\n// dispatch utility (taken from ranges-v3)\ntemplate<unsigned N> struct priority_tag : priority_tag < N - 1 > {};\ntemplate<> struct priority_tag<0> {};\n\n// taken from ranges-v3\ntemplate<typename T>\nstruct static_const\n{\n    static constexpr T value{};\n};\n\ntemplate<typename T>\nconstexpr T static_const<T>::value;\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\n#include <ciso646> // not\n#include <limits> // numeric_limits\n#include <type_traits> // false_type, is_constructible, is_integral, is_same, true_type\n#include <utility> // declval\n\n// #include <nlohmann/json_fwd.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n\n#include <type_traits>\n\n// #include <nlohmann/detail/meta/void_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate <typename ...Ts> struct make_void\n{\n    using type = void;\n};\ntemplate <typename ...Ts> using void_t = typename make_void<Ts...>::type;\n} // namespace detail\n}  // namespace nlohmann\n\n\n// http://en.cppreference.com/w/cpp/experimental/is_detected\nnamespace nlohmann\n{\nnamespace detail\n{\nstruct nonesuch\n{\n    nonesuch() = delete;\n    ~nonesuch() = delete;\n    nonesuch(nonesuch const&) = delete;\n    void operator=(nonesuch const&) = delete;\n};\n\ntemplate <class Default,\n          class AlwaysVoid,\n          template <class...> class Op,\n          class... Args>\nstruct detector\n{\n    using value_t = std::false_type;\n    using type = Default;\n};\n\ntemplate <class Default, template <class...> class Op, class... Args>\nstruct detector<Default, void_t<Op<Args...>>, Op, Args...>\n{\n    using value_t = std::true_type;\n    using type = Op<Args...>;\n};\n\ntemplate <template <class...> class Op, class... Args>\nusing is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;\n\ntemplate <template <class...> class Op, class... Args>\nusing detected_t = typename detector<nonesuch, void, Op, Args...>::type;\n\ntemplate <class Default, template <class...> class Op, class... Args>\nusing detected_or = detector<Default, void, Op, Args...>;\n\ntemplate <class Default, template <class...> class Op, class... Args>\nusing detected_or_t = typename detected_or<Default, Op, Args...>::type;\n\ntemplate <class Expected, template <class...> class Op, class... Args>\nusing is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;\n\ntemplate <class To, template <class...> class Op, class... Args>\nusing is_detected_convertible =\n    std::is_convertible<detected_t<Op, Args...>, To>;\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\n/*!\n@brief detail namespace with internal helper functions\n\nThis namespace collects functions that should not be exposed,\nimplementations of some @ref basic_json methods, and meta-programming helpers.\n\n@since version 2.1.0\n*/\nnamespace detail\n{\n/////////////\n// helpers //\n/////////////\n\n// Note to maintainers:\n//\n// Every trait in this file expects a non CV-qualified type.\n// The only exceptions are in the 'aliases for detected' section\n// (i.e. those of the form: decltype(T::member_function(std::declval<T>())))\n//\n// In this case, T has to be properly CV-qualified to constraint the function arguments\n// (e.g. to_json(BasicJsonType&, const T&))\n\ntemplate<typename> struct is_basic_json : std::false_type {};\n\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nstruct is_basic_json<NLOHMANN_BASIC_JSON_TPL> : std::true_type {};\n\n//////////////////////////\n// aliases for detected //\n//////////////////////////\n\ntemplate <typename T>\nusing mapped_type_t = typename T::mapped_type;\n\ntemplate <typename T>\nusing key_type_t = typename T::key_type;\n\ntemplate <typename T>\nusing value_type_t = typename T::value_type;\n\ntemplate <typename T>\nusing difference_type_t = typename T::difference_type;\n\ntemplate <typename T>\nusing pointer_t = typename T::pointer;\n\ntemplate <typename T>\nusing reference_t = typename T::reference;\n\ntemplate <typename T>\nusing iterator_category_t = typename T::iterator_category;\n\ntemplate <typename T>\nusing iterator_t = typename T::iterator;\n\ntemplate <typename T, typename... Args>\nusing to_json_function = decltype(T::to_json(std::declval<Args>()...));\n\ntemplate <typename T, typename... Args>\nusing from_json_function = decltype(T::from_json(std::declval<Args>()...));\n\ntemplate <typename T, typename U>\nusing get_template_function = decltype(std::declval<T>().template get<U>());\n\n// trait checking if JSONSerializer<T>::from_json(json const&, udt&) exists\ntemplate <typename BasicJsonType, typename T, typename = void>\nstruct has_from_json : std::false_type {};\n\ntemplate <typename BasicJsonType, typename T>\nstruct has_from_json<BasicJsonType, T,\n           enable_if_t<not is_basic_json<T>::value>>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, from_json_function, serializer,\n        const BasicJsonType&, T&>::value;\n};\n\n// This trait checks if JSONSerializer<T>::from_json(json const&) exists\n// this overload is used for non-default-constructible user-defined-types\ntemplate <typename BasicJsonType, typename T, typename = void>\nstruct has_non_default_from_json : std::false_type {};\n\ntemplate<typename BasicJsonType, typename T>\nstruct has_non_default_from_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<T, from_json_function, serializer,\n        const BasicJsonType&>::value;\n};\n\n// This trait checks if BasicJsonType::json_serializer<T>::to_json exists\n// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.\ntemplate <typename BasicJsonType, typename T, typename = void>\nstruct has_to_json : std::false_type {};\n\ntemplate <typename BasicJsonType, typename T>\nstruct has_to_json<BasicJsonType, T, enable_if_t<not is_basic_json<T>::value>>\n{\n    using serializer = typename BasicJsonType::template json_serializer<T, void>;\n\n    static constexpr bool value =\n        is_detected_exact<void, to_json_function, serializer, BasicJsonType&,\n        T>::value;\n};\n\n\n///////////////////\n// is_ functions //\n///////////////////\n\ntemplate <typename T, typename = void>\nstruct is_iterator_traits : std::false_type {};\n\ntemplate <typename T>\nstruct is_iterator_traits<std::iterator_traits<T>>\n{\n  private:\n    using traits = std::iterator_traits<T>;\n\n  public:\n    static constexpr auto value =\n        is_detected<value_type_t, traits>::value &&\n        is_detected<difference_type_t, traits>::value &&\n        is_detected<pointer_t, traits>::value &&\n        is_detected<iterator_category_t, traits>::value &&\n        is_detected<reference_t, traits>::value;\n};\n\n// source: https://stackoverflow.com/a/37193089/4116453\n\ntemplate <typename T, typename = void>\nstruct is_complete_type : std::false_type {};\n\ntemplate <typename T>\nstruct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};\n\ntemplate <typename BasicJsonType, typename CompatibleObjectType,\n          typename = void>\nstruct is_compatible_object_type_impl : std::false_type {};\n\ntemplate <typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type_impl <\n    BasicJsonType, CompatibleObjectType,\n    enable_if_t<is_detected<mapped_type_t, CompatibleObjectType>::value and\n    is_detected<key_type_t, CompatibleObjectType>::value >>\n{\n\n    using object_t = typename BasicJsonType::object_t;\n\n    // macOS's is_constructible does not play well with nonesuch...\n    static constexpr bool value =\n        std::is_constructible<typename object_t::key_type,\n        typename CompatibleObjectType::key_type>::value and\n        std::is_constructible<typename object_t::mapped_type,\n        typename CompatibleObjectType::mapped_type>::value;\n};\n\ntemplate <typename BasicJsonType, typename CompatibleObjectType>\nstruct is_compatible_object_type\n    : is_compatible_object_type_impl<BasicJsonType, CompatibleObjectType> {};\n\ntemplate <typename BasicJsonType, typename ConstructibleObjectType,\n          typename = void>\nstruct is_constructible_object_type_impl : std::false_type {};\n\ntemplate <typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type_impl <\n    BasicJsonType, ConstructibleObjectType,\n    enable_if_t<is_detected<mapped_type_t, ConstructibleObjectType>::value and\n    is_detected<key_type_t, ConstructibleObjectType>::value >>\n{\n    using object_t = typename BasicJsonType::object_t;\n\n    static constexpr bool value =\n        (std::is_constructible<typename ConstructibleObjectType::key_type, typename object_t::key_type>::value and\n         std::is_same<typename object_t::mapped_type, typename ConstructibleObjectType::mapped_type>::value) or\n        (has_from_json<BasicJsonType, typename ConstructibleObjectType::mapped_type>::value or\n         has_non_default_from_json<BasicJsonType, typename ConstructibleObjectType::mapped_type >::value);\n};\n\ntemplate <typename BasicJsonType, typename ConstructibleObjectType>\nstruct is_constructible_object_type\n    : is_constructible_object_type_impl<BasicJsonType,\n      ConstructibleObjectType> {};\n\ntemplate <typename BasicJsonType, typename CompatibleStringType,\n          typename = void>\nstruct is_compatible_string_type_impl : std::false_type {};\n\ntemplate <typename BasicJsonType, typename CompatibleStringType>\nstruct is_compatible_string_type_impl <\n    BasicJsonType, CompatibleStringType,\n    enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,\n    value_type_t, CompatibleStringType>::value >>\n{\n    static constexpr auto value =\n        std::is_constructible<typename BasicJsonType::string_t, CompatibleStringType>::value;\n};\n\ntemplate <typename BasicJsonType, typename ConstructibleStringType>\nstruct is_compatible_string_type\n    : is_compatible_string_type_impl<BasicJsonType, ConstructibleStringType> {};\n\ntemplate <typename BasicJsonType, typename ConstructibleStringType,\n          typename = void>\nstruct is_constructible_string_type_impl : std::false_type {};\n\ntemplate <typename BasicJsonType, typename ConstructibleStringType>\nstruct is_constructible_string_type_impl <\n    BasicJsonType, ConstructibleStringType,\n    enable_if_t<is_detected_exact<typename BasicJsonType::string_t::value_type,\n    value_type_t, ConstructibleStringType>::value >>\n{\n    static constexpr auto value =\n        std::is_constructible<ConstructibleStringType,\n        typename BasicJsonType::string_t>::value;\n};\n\ntemplate <typename BasicJsonType, typename ConstructibleStringType>\nstruct is_constructible_string_type\n    : is_constructible_string_type_impl<BasicJsonType, ConstructibleStringType> {};\n\ntemplate <typename BasicJsonType, typename CompatibleArrayType, typename = void>\nstruct is_compatible_array_type_impl : std::false_type {};\n\ntemplate <typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type_impl <\n    BasicJsonType, CompatibleArrayType,\n    enable_if_t<is_detected<value_type_t, CompatibleArrayType>::value and\n    is_detected<iterator_t, CompatibleArrayType>::value and\n// This is needed because json_reverse_iterator has a ::iterator type...\n// Therefore it is detected as a CompatibleArrayType.\n// The real fix would be to have an Iterable concept.\n    not is_iterator_traits<\n    std::iterator_traits<CompatibleArrayType>>::value >>\n{\n    static constexpr bool value =\n        std::is_constructible<BasicJsonType,\n        typename CompatibleArrayType::value_type>::value;\n};\n\ntemplate <typename BasicJsonType, typename CompatibleArrayType>\nstruct is_compatible_array_type\n    : is_compatible_array_type_impl<BasicJsonType, CompatibleArrayType> {};\n\ntemplate <typename BasicJsonType, typename ConstructibleArrayType, typename = void>\nstruct is_constructible_array_type_impl : std::false_type {};\n\ntemplate <typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t<std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value >>\n            : std::true_type {};\n\ntemplate <typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type_impl <\n    BasicJsonType, ConstructibleArrayType,\n    enable_if_t<not std::is_same<ConstructibleArrayType,\n    typename BasicJsonType::value_type>::value and\n    is_detected<value_type_t, ConstructibleArrayType>::value and\n    is_detected<iterator_t, ConstructibleArrayType>::value and\n    is_complete_type<\n    detected_t<value_type_t, ConstructibleArrayType>>::value >>\n{\n    static constexpr bool value =\n        // This is needed because json_reverse_iterator has a ::iterator type,\n        // furthermore, std::back_insert_iterator (and other iterators) have a base class `iterator`...\n        // Therefore it is detected as a ConstructibleArrayType.\n        // The real fix would be to have an Iterable concept.\n        not is_iterator_traits <\n        std::iterator_traits<ConstructibleArrayType >>::value and\n\n        (std::is_same<typename ConstructibleArrayType::value_type, typename BasicJsonType::array_t::value_type>::value or\n         has_from_json<BasicJsonType,\n         typename ConstructibleArrayType::value_type>::value or\n         has_non_default_from_json <\n         BasicJsonType, typename ConstructibleArrayType::value_type >::value);\n};\n\ntemplate <typename BasicJsonType, typename ConstructibleArrayType>\nstruct is_constructible_array_type\n    : is_constructible_array_type_impl<BasicJsonType, ConstructibleArrayType> {};\n\ntemplate <typename RealIntegerType, typename CompatibleNumberIntegerType,\n          typename = void>\nstruct is_compatible_integer_type_impl : std::false_type {};\n\ntemplate <typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type_impl <\n    RealIntegerType, CompatibleNumberIntegerType,\n    enable_if_t<std::is_integral<RealIntegerType>::value and\n    std::is_integral<CompatibleNumberIntegerType>::value and\n    not std::is_same<bool, CompatibleNumberIntegerType>::value >>\n{\n    // is there an assert somewhere on overflows?\n    using RealLimits = std::numeric_limits<RealIntegerType>;\n    using CompatibleLimits = std::numeric_limits<CompatibleNumberIntegerType>;\n\n    static constexpr auto value =\n        std::is_constructible<RealIntegerType,\n        CompatibleNumberIntegerType>::value and\n        CompatibleLimits::is_integer and\n        RealLimits::is_signed == CompatibleLimits::is_signed;\n};\n\ntemplate <typename RealIntegerType, typename CompatibleNumberIntegerType>\nstruct is_compatible_integer_type\n    : is_compatible_integer_type_impl<RealIntegerType,\n      CompatibleNumberIntegerType> {};\n\ntemplate <typename BasicJsonType, typename CompatibleType, typename = void>\nstruct is_compatible_type_impl: std::false_type {};\n\ntemplate <typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type_impl <\n    BasicJsonType, CompatibleType,\n    enable_if_t<is_complete_type<CompatibleType>::value >>\n{\n    static constexpr bool value =\n        has_to_json<BasicJsonType, CompatibleType>::value;\n};\n\ntemplate <typename BasicJsonType, typename CompatibleType>\nstruct is_compatible_type\n    : is_compatible_type_impl<BasicJsonType, CompatibleType> {};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n\n#include <exception> // exception\n#include <stdexcept> // runtime_error\n#include <string> // to_string\n\n// #include <nlohmann/detail/input/position_t.hpp>\n\n\n#include <cstddef> // size_t\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// struct to capture the start position of the current token\nstruct position_t\n{\n    /// the total number of characters read\n    std::size_t chars_read_total = 0;\n    /// the number of characters read in the current line\n    std::size_t chars_read_current_line = 0;\n    /// the number of lines read\n    std::size_t lines_read = 0;\n\n    /// conversion to size_t to preserve SAX interface\n    constexpr operator size_t() const\n    {\n        return chars_read_total;\n    }\n};\n\n}\n}\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n////////////////\n// exceptions //\n////////////////\n\n/*!\n@brief general exception of the @ref basic_json class\n\nThis class is an extension of `std::exception` objects with a member @a id for\nexception ids. It is used as the base class for all exceptions thrown by the\n@ref basic_json class. This class can hence be used as \"wildcard\" to catch\nexceptions.\n\nSubclasses:\n- @ref parse_error for exceptions indicating a parse error\n- @ref invalid_iterator for exceptions indicating errors with iterators\n- @ref type_error for exceptions indicating executing a member function with\n                  a wrong type\n- @ref out_of_range for exceptions indicating access out of the defined range\n- @ref other_error for exceptions indicating other library errors\n\n@internal\n@note To have nothrow-copy-constructible exceptions, we internally use\n      `std::runtime_error` which can cope with arbitrary-length error messages.\n      Intermediate strings are built with static functions and then passed to\n      the actual constructor.\n@endinternal\n\n@liveexample{The following code shows how arbitrary library exceptions can be\ncaught.,exception}\n\n@since version 3.0.0\n*/\nclass exception : public std::exception\n{\n  public:\n    /// returns the explanatory string\n    const char* what() const noexcept override\n    {\n        return m.what();\n    }\n\n    /// the id of the exception\n    const int id;\n\n  protected:\n    exception(int id_, const char* what_arg) : id(id_), m(what_arg) {}\n\n    static std::string name(const std::string& ename, int id_)\n    {\n        return \"[json.exception.\" + ename + \".\" + std::to_string(id_) + \"] \";\n    }\n\n  private:\n    /// an exception object as storage for error messages\n    std::runtime_error m;\n};\n\n/*!\n@brief exception indicating a parse error\n\nThis exception is thrown by the library when a parse error occurs. Parse errors\ncan occur during the deserialization of JSON text, CBOR, MessagePack, as well\nas when using JSON Patch.\n\nMember @a byte holds the byte index of the last read character in the input\nfile.\n\nExceptions have ids 1xx.\n\nname / id                      | example message | description\n------------------------------ | --------------- | -------------------------\njson.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position.\njson.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\\uxxxx` entries (\"surrogate pairs\"). This error indicates that the surrogate pair is incomplete or contains an invalid code point.\njson.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid.\njson.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects.\njson.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one \"op\" member, whose value indicates the operation to perform. Its value must be one of \"add\", \"remove\", \"replace\", \"move\", \"copy\", or \"test\"; other values are errors.\njson.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`.\njson.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character.\njson.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences.\njson.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number.\njson.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read.\njson.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read.\njson.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read.\njson.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet).\n\n@note For an input with n bytes, 1 is the index of the first character and n+1\n      is the index of the terminating null byte or the end of file. This also\n      holds true when reading a byte vector (CBOR or MessagePack).\n\n@liveexample{The following code shows how a `parse_error` exception can be\ncaught.,parse_error}\n\n@sa @ref exception for the base class of the library exceptions\n@sa @ref invalid_iterator for exceptions indicating errors with iterators\n@sa @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa @ref out_of_range for exceptions indicating access out of the defined range\n@sa @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass parse_error : public exception\n{\n  public:\n    /*!\n    @brief create a parse error exception\n    @param[in] id_       the id of the exception\n    @param[in] position  the position where the error occurred (or with\n                         chars_read_total=0 if the position cannot be\n                         determined)\n    @param[in] what_arg  the explanatory string\n    @return parse_error object\n    */\n    static parse_error create(int id_, const position_t& pos, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"parse_error\", id_) + \"parse error\" +\n                        position_string(pos) + \": \" + what_arg;\n        return parse_error(id_, pos.chars_read_total, w.c_str());\n    }\n\n    static parse_error create(int id_, std::size_t byte_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"parse_error\", id_) + \"parse error\" +\n                        (byte_ != 0 ? (\" at byte \" + std::to_string(byte_)) : \"\") +\n                        \": \" + what_arg;\n        return parse_error(id_, byte_, w.c_str());\n    }\n\n    /*!\n    @brief byte index of the parse error\n\n    The byte index of the last read character in the input file.\n\n    @note For an input with n bytes, 1 is the index of the first character and\n          n+1 is the index of the terminating null byte or the end of file.\n          This also holds true when reading a byte vector (CBOR or MessagePack).\n    */\n    const std::size_t byte;\n\n  private:\n    parse_error(int id_, std::size_t byte_, const char* what_arg)\n        : exception(id_, what_arg), byte(byte_) {}\n\n    static std::string position_string(const position_t& pos)\n    {\n        return \" at line \" + std::to_string(pos.lines_read + 1) +\n               \", column \" + std::to_string(pos.chars_read_current_line);\n    }\n};\n\n/*!\n@brief exception indicating errors with iterators\n\nThis exception is thrown if iterators passed to a library function do not match\nthe expected semantics.\n\nExceptions have ids 2xx.\n\nname / id                           | example message | description\n----------------------------------- | --------------- | -------------------------\njson.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.\njson.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.\njson.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.\njson.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.\njson.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.\njson.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.\njson.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.\njson.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.\njson.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.\njson.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.\njson.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to.\njson.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container.\njson.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered.\njson.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().\n\n@liveexample{The following code shows how an `invalid_iterator` exception can be\ncaught.,invalid_iterator}\n\n@sa @ref exception for the base class of the library exceptions\n@sa @ref parse_error for exceptions indicating a parse error\n@sa @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa @ref out_of_range for exceptions indicating access out of the defined range\n@sa @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass invalid_iterator : public exception\n{\n  public:\n    static invalid_iterator create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"invalid_iterator\", id_) + what_arg;\n        return invalid_iterator(id_, w.c_str());\n    }\n\n  private:\n    invalid_iterator(int id_, const char* what_arg)\n        : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating executing a member function with a wrong type\n\nThis exception is thrown in case of a type error; that is, a library function is\nexecuted on a JSON value whose type does not match the expected semantics.\n\nExceptions have ids 3xx.\n\nname / id                     | example message | description\n----------------------------- | --------------- | -------------------------\njson.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead.\njson.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types.\njson.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t&.\njson.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types.\njson.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types.\njson.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types.\njson.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types.\njson.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types.\njson.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types.\njson.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types.\njson.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types.\njson.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types.\njson.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined.\njson.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers.\njson.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive.\njson.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. |\njson.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) |\n\n@liveexample{The following code shows how a `type_error` exception can be\ncaught.,type_error}\n\n@sa @ref exception for the base class of the library exceptions\n@sa @ref parse_error for exceptions indicating a parse error\n@sa @ref invalid_iterator for exceptions indicating errors with iterators\n@sa @ref out_of_range for exceptions indicating access out of the defined range\n@sa @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass type_error : public exception\n{\n  public:\n    static type_error create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"type_error\", id_) + what_arg;\n        return type_error(id_, w.c_str());\n    }\n\n  private:\n    type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating access out of the defined range\n\nThis exception is thrown in case a library function is called on an input\nparameter that exceeds the expected range, for instance in case of array\nindices or nonexisting object keys.\n\nExceptions have ids 4xx.\n\nname / id                       | example message | description\n------------------------------- | --------------- | -------------------------\njson.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1.\njson.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it.\njson.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object.\njson.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved.\njson.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value.\njson.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF.\njson.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. |\njson.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. |\njson.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string |\n\n@liveexample{The following code shows how an `out_of_range` exception can be\ncaught.,out_of_range}\n\n@sa @ref exception for the base class of the library exceptions\n@sa @ref parse_error for exceptions indicating a parse error\n@sa @ref invalid_iterator for exceptions indicating errors with iterators\n@sa @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa @ref other_error for exceptions indicating other library errors\n\n@since version 3.0.0\n*/\nclass out_of_range : public exception\n{\n  public:\n    static out_of_range create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"out_of_range\", id_) + what_arg;\n        return out_of_range(id_, w.c_str());\n    }\n\n  private:\n    out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n\n/*!\n@brief exception indicating other library errors\n\nThis exception is thrown in case of errors that cannot be classified with the\nother exception types.\n\nExceptions have ids 5xx.\n\nname / id                      | example message | description\n------------------------------ | --------------- | -------------------------\njson.exception.other_error.501 | unsuccessful: {\"op\":\"test\",\"path\":\"/baz\", \"value\":\"bar\"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed.\n\n@sa @ref exception for the base class of the library exceptions\n@sa @ref parse_error for exceptions indicating a parse error\n@sa @ref invalid_iterator for exceptions indicating errors with iterators\n@sa @ref type_error for exceptions indicating executing a member function with\n                    a wrong type\n@sa @ref out_of_range for exceptions indicating access out of the defined range\n\n@liveexample{The following code shows how an `other_error` exception can be\ncaught.,other_error}\n\n@since version 3.0.0\n*/\nclass other_error : public exception\n{\n  public:\n    static other_error create(int id_, const std::string& what_arg)\n    {\n        std::string w = exception::name(\"other_error\", id_) + what_arg;\n        return other_error(id_, w.c_str());\n    }\n\n  private:\n    other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\n#include <array> // array\n#include <ciso646> // and\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////////////\n// JSON type enumeration //\n///////////////////////////\n\n/*!\n@brief the JSON type enumeration\n\nThis enumeration collects the different JSON types. It is internally used to\ndistinguish the stored values, and the functions @ref basic_json::is_null(),\n@ref basic_json::is_object(), @ref basic_json::is_array(),\n@ref basic_json::is_string(), @ref basic_json::is_boolean(),\n@ref basic_json::is_number() (with @ref basic_json::is_number_integer(),\n@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()),\n@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and\n@ref basic_json::is_structured() rely on it.\n\n@note There are three enumeration entries (number_integer, number_unsigned, and\nnumber_float), because the library distinguishes these three types for numbers:\n@ref basic_json::number_unsigned_t is used for unsigned integers,\n@ref basic_json::number_integer_t is used for signed integers, and\n@ref basic_json::number_float_t is used for floating-point numbers or to\napproximate integers which do not fit in the limits of their respective type.\n\n@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON\nvalue with the default value for a given type\n\n@since version 1.0.0\n*/\nenum class value_t : std::uint8_t\n{\n    null,             ///< null value\n    object,           ///< object (unordered set of name/value pairs)\n    array,            ///< array (ordered collection of values)\n    string,           ///< string value\n    boolean,          ///< boolean value\n    number_integer,   ///< number value (signed integer)\n    number_unsigned,  ///< number value (unsigned integer)\n    number_float,     ///< number value (floating-point)\n    discarded         ///< discarded by the the parser callback function\n};\n\n/*!\n@brief comparison operator for JSON types\n\nReturns an ordering that is similar to Python:\n- order: null < boolean < number < object < array < string\n- furthermore, each type is not smaller than itself\n- discarded values are not comparable\n\n@since version 1.0.0\n*/\ninline bool operator<(const value_t lhs, const value_t rhs) noexcept\n{\n    static constexpr std::array<std::uint8_t, 8> order = {{\n            0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */,\n            1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */\n        }\n    };\n\n    const auto l_index = static_cast<std::size_t>(lhs);\n    const auto r_index = static_cast<std::size_t>(rhs);\n    return l_index < order.size() and r_index < order.size() and order[l_index] < order[r_index];\n}\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/conversions/from_json.hpp>\n\n\n#include <algorithm> // transform\n#include <array> // array\n#include <ciso646> // and, not\n#include <forward_list> // forward_list\n#include <iterator> // inserter, front_inserter, end\n#include <map> // map\n#include <string> // string\n#include <tuple> // tuple, make_tuple\n#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible\n#include <unordered_map> // unordered_map\n#include <utility> // pair, declval\n#include <valarray> // valarray\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename std::nullptr_t& n)\n{\n    if (JSON_UNLIKELY(not j.is_null()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be null, but is \" + std::string(j.type_name())));\n    }\n    n = nullptr;\n}\n\n// overloads for basic_json template parameters\ntemplate<typename BasicJsonType, typename ArithmeticType,\n         enable_if_t<std::is_arithmetic<ArithmeticType>::value and\n                     not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n                     int> = 0>\nvoid get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n\n        default:\n            JSON_THROW(type_error::create(302, \"type must be number, but is \" + std::string(j.type_name())));\n    }\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)\n{\n    if (JSON_UNLIKELY(not j.is_boolean()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be boolean, but is \" + std::string(j.type_name())));\n    }\n    b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)\n{\n    if (JSON_UNLIKELY(not j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be string, but is \" + std::string(j.type_name())));\n    }\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate <\n    typename BasicJsonType, typename ConstructibleStringType,\n    enable_if_t <\n        is_constructible_string_type<BasicJsonType, ConstructibleStringType>::value and\n        not std::is_same<typename BasicJsonType::string_t,\n                         ConstructibleStringType>::value,\n        int > = 0 >\nvoid from_json(const BasicJsonType& j, ConstructibleStringType& s)\n{\n    if (JSON_UNLIKELY(not j.is_string()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be string, but is \" + std::string(j.type_name())));\n    }\n\n    s = *j.template get_ptr<const typename BasicJsonType::string_t*>();\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)\n{\n    get_arithmetic_value(j, val);\n}\n\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, EnumType& e)\n{\n    typename std::underlying_type<EnumType>::type val;\n    get_arithmetic_value(j, val);\n    e = static_cast<EnumType>(val);\n}\n\n// forward_list doesn't have an insert method\ntemplate<typename BasicJsonType, typename T, typename Allocator,\n         enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)\n{\n    if (JSON_UNLIKELY(not j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    std::transform(j.rbegin(), j.rend(),\n                   std::front_inserter(l), [](const BasicJsonType & i)\n    {\n        return i.template get<T>();\n    });\n}\n\n// valarray doesn't have an insert method\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_convertible<BasicJsonType, T>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, std::valarray<T>& l)\n{\n    if (JSON_UNLIKELY(not j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    l.resize(j.size());\n    std::copy(j.m_value.array->begin(), j.m_value.array->end(), std::begin(l));\n}\n\ntemplate<typename BasicJsonType>\nvoid from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)\n{\n    arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();\n}\n\ntemplate <typename BasicJsonType, typename T, std::size_t N>\nauto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,\n                          priority_tag<2> /*unused*/)\n-> decltype(j.template get<T>(), void())\n{\n    for (std::size_t i = 0; i < N; ++i)\n    {\n        arr[i] = j.at(i).template get<T>();\n    }\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleArrayType>\nauto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)\n-> decltype(\n    arr.reserve(std::declval<typename ConstructibleArrayType::size_type>()),\n    j.template get<typename ConstructibleArrayType::value_type>(),\n    void())\n{\n    using std::end;\n\n    arr.reserve(j.size());\n    std::transform(j.begin(), j.end(),\n                   std::inserter(arr, end(arr)), [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n}\n\ntemplate <typename BasicJsonType, typename ConstructibleArrayType>\nvoid from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr,\n                          priority_tag<0> /*unused*/)\n{\n    using std::end;\n\n    std::transform(\n        j.begin(), j.end(), std::inserter(arr, end(arr)),\n        [](const BasicJsonType & i)\n    {\n        // get<BasicJsonType>() returns *this, this won't call a from_json\n        // method when value_type is BasicJsonType\n        return i.template get<typename ConstructibleArrayType::value_type>();\n    });\n}\n\ntemplate <typename BasicJsonType, typename ConstructibleArrayType,\n          enable_if_t <\n              is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value and\n              not is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value and\n              not is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value and\n              not is_basic_json<ConstructibleArrayType>::value,\n              int > = 0 >\n\nauto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)\n-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),\nj.template get<typename ConstructibleArrayType::value_type>(),\nvoid())\n{\n    if (JSON_UNLIKELY(not j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" +\n                                      std::string(j.type_name())));\n    }\n\n    from_json_array_impl(j, arr, priority_tag<3> {});\n}\n\ntemplate<typename BasicJsonType, typename ConstructibleObjectType,\n         enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>\nvoid from_json(const BasicJsonType& j, ConstructibleObjectType& obj)\n{\n    if (JSON_UNLIKELY(not j.is_object()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be object, but is \" + std::string(j.type_name())));\n    }\n\n    auto inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();\n    using value_type = typename ConstructibleObjectType::value_type;\n    std::transform(\n        inner_object->begin(), inner_object->end(),\n        std::inserter(obj, obj.begin()),\n        [](typename BasicJsonType::object_t::value_type const & p)\n    {\n        return value_type(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());\n    });\n}\n\n// overload for arithmetic types, not chosen for basic_json template arguments\n// (BooleanType, etc..); note: Is it really necessary to provide explicit\n// overloads for boolean_t etc. in case of a custom BooleanType which is not\n// an arithmetic type?\ntemplate<typename BasicJsonType, typename ArithmeticType,\n         enable_if_t <\n             std::is_arithmetic<ArithmeticType>::value and\n             not std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value and\n             not std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value and\n             not std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value and\n             not std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,\n             int> = 0>\nvoid from_json(const BasicJsonType& j, ArithmeticType& val)\n{\n    switch (static_cast<value_t>(j))\n    {\n        case value_t::number_unsigned:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());\n            break;\n        }\n        case value_t::number_integer:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());\n            break;\n        }\n        case value_t::number_float:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());\n            break;\n        }\n        case value_t::boolean:\n        {\n            val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());\n            break;\n        }\n\n        default:\n            JSON_THROW(type_error::create(302, \"type must be number, but is \" + std::string(j.type_name())));\n    }\n}\n\ntemplate<typename BasicJsonType, typename A1, typename A2>\nvoid from_json(const BasicJsonType& j, std::pair<A1, A2>& p)\n{\n    p = {j.at(0).template get<A1>(), j.at(1).template get<A2>()};\n}\n\ntemplate<typename BasicJsonType, typename Tuple, std::size_t... Idx>\nvoid from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence<Idx...> /*unused*/)\n{\n    t = std::make_tuple(j.at(Idx).template get<typename std::tuple_element<Idx, Tuple>::type>()...);\n}\n\ntemplate<typename BasicJsonType, typename... Args>\nvoid from_json(const BasicJsonType& j, std::tuple<Args...>& t)\n{\n    from_json_tuple_impl(j, t, index_sequence_for<Args...> {});\n}\n\ntemplate <typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,\n          typename = enable_if_t<not std::is_constructible<\n                                     typename BasicJsonType::string_t, Key>::value>>\nvoid from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)\n{\n    if (JSON_UNLIKELY(not j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    for (const auto& p : j)\n    {\n        if (JSON_UNLIKELY(not p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(p.type_name())));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\ntemplate <typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,\n          typename = enable_if_t<not std::is_constructible<\n                                     typename BasicJsonType::string_t, Key>::value>>\nvoid from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)\n{\n    if (JSON_UNLIKELY(not j.is_array()))\n    {\n        JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(j.type_name())));\n    }\n    for (const auto& p : j)\n    {\n        if (JSON_UNLIKELY(not p.is_array()))\n        {\n            JSON_THROW(type_error::create(302, \"type must be array, but is \" + std::string(p.type_name())));\n        }\n        m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());\n    }\n}\n\nstruct from_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(const BasicJsonType& j, T& val) const\n    noexcept(noexcept(from_json(j, val)))\n    -> decltype(from_json(j, val), void())\n    {\n        return from_json(j, val);\n    }\n};\n}  // namespace detail\n\n/// namespace to hold default `from_json` function\n/// to see why this is required:\n/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html\nnamespace\n{\nconstexpr const auto& from_json = detail::static_const<detail::from_json_fn>::value;\n} // namespace\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/conversions/to_json.hpp>\n\n\n#include <ciso646> // or, and, not\n#include <iterator> // begin, end\n#include <tuple> // tuple, get\n#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type\n#include <utility> // move, forward, declval, pair\n#include <valarray> // valarray\n#include <vector> // vector\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n// #include <nlohmann/detail/iterators/iteration_proxy.hpp>\n\n\n#include <cstddef> // size_t\n#include <string> // string, to_string\n#include <iterator> // input_iterator_tag\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// proxy class for the items() function\ntemplate<typename IteratorType> class iteration_proxy\n{\n  private:\n    /// helper class for iteration\n    class iteration_proxy_internal\n    {\n      public:\n        using difference_type = std::ptrdiff_t;\n        using value_type = iteration_proxy_internal;\n        using pointer = iteration_proxy_internal*;\n        using reference = iteration_proxy_internal&;\n        using iterator_category = std::input_iterator_tag;\n\n      private:\n        /// the iterator\n        IteratorType anchor;\n        /// an index for arrays (used to create key names)\n        std::size_t array_index = 0;\n        /// last stringified array index\n        mutable std::size_t array_index_last = 0;\n        /// a string representation of the array index\n        mutable std::string array_index_str = \"0\";\n        /// an empty string (to return a reference for primitive values)\n        const std::string empty_str = \"\";\n\n      public:\n        explicit iteration_proxy_internal(IteratorType it) noexcept : anchor(it) {}\n\n        /// dereference operator (needed for range-based for)\n        iteration_proxy_internal& operator*()\n        {\n            return *this;\n        }\n\n        /// increment operator (needed for range-based for)\n        iteration_proxy_internal& operator++()\n        {\n            ++anchor;\n            ++array_index;\n\n            return *this;\n        }\n\n        /// equality operator (needed for InputIterator)\n        bool operator==(const iteration_proxy_internal& o) const noexcept\n        {\n            return anchor == o.anchor;\n        }\n\n        /// inequality operator (needed for range-based for)\n        bool operator!=(const iteration_proxy_internal& o) const noexcept\n        {\n            return anchor != o.anchor;\n        }\n\n        /// return key of the iterator\n        const std::string& key() const\n        {\n            assert(anchor.m_object != nullptr);\n\n            switch (anchor.m_object->type())\n            {\n                // use integer array index as key\n                case value_t::array:\n                {\n                    if (array_index != array_index_last)\n                    {\n                        array_index_str = std::to_string(array_index);\n                        array_index_last = array_index;\n                    }\n                    return array_index_str;\n                }\n\n                // use key from the object\n                case value_t::object:\n                    return anchor.key();\n\n                // use an empty key for all primitive types\n                default:\n                    return empty_str;\n            }\n        }\n\n        /// return value of the iterator\n        typename IteratorType::reference value() const\n        {\n            return anchor.value();\n        }\n    };\n\n    /// the container to iterate\n    typename IteratorType::reference container;\n\n  public:\n    /// construct iteration proxy from a container\n    explicit iteration_proxy(typename IteratorType::reference cont) noexcept\n        : container(cont) {}\n\n    /// return iterator begin (needed for range-based for)\n    iteration_proxy_internal begin() noexcept\n    {\n        return iteration_proxy_internal(container.begin());\n    }\n\n    /// return iterator end (needed for range-based for)\n    iteration_proxy_internal end() noexcept\n    {\n        return iteration_proxy_internal(container.end());\n    }\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n//////////////////\n// constructors //\n//////////////////\n\ntemplate<value_t> struct external_constructor;\n\ntemplate<>\nstruct external_constructor<value_t::boolean>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept\n    {\n        j.m_type = value_t::boolean;\n        j.m_value = b;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::string>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)\n    {\n        j.m_type = value_t::string;\n        j.m_value = s;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n    {\n        j.m_type = value_t::string;\n        j.m_value = std::move(s);\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType, typename CompatibleStringType,\n             enable_if_t<not std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,\n                         int> = 0>\n    static void construct(BasicJsonType& j, const CompatibleStringType& str)\n    {\n        j.m_type = value_t::string;\n        j.m_value.string = j.template create<typename BasicJsonType::string_t>(str);\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_float>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept\n    {\n        j.m_type = value_t::number_float;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_unsigned>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept\n    {\n        j.m_type = value_t::number_unsigned;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::number_integer>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept\n    {\n        j.m_type = value_t::number_integer;\n        j.m_value = val;\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::array>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = arr;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = std::move(arr);\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType, typename CompatibleArrayType,\n             enable_if_t<not std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value,\n                         int> = 0>\n    static void construct(BasicJsonType& j, const CompatibleArrayType& arr)\n    {\n        using std::begin;\n        using std::end;\n        j.m_type = value_t::array;\n        j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const std::vector<bool>& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = value_t::array;\n        j.m_value.array->reserve(arr.size());\n        for (const bool x : arr)\n        {\n            j.m_value.array->push_back(x);\n        }\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType, typename T,\n             enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\n    static void construct(BasicJsonType& j, const std::valarray<T>& arr)\n    {\n        j.m_type = value_t::array;\n        j.m_value = value_t::array;\n        j.m_value.array->resize(arr.size());\n        std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());\n        j.assert_invariant();\n    }\n};\n\ntemplate<>\nstruct external_constructor<value_t::object>\n{\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)\n    {\n        j.m_type = value_t::object;\n        j.m_value = obj;\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType>\n    static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n    {\n        j.m_type = value_t::object;\n        j.m_value = std::move(obj);\n        j.assert_invariant();\n    }\n\n    template<typename BasicJsonType, typename CompatibleObjectType,\n             enable_if_t<not std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int> = 0>\n    static void construct(BasicJsonType& j, const CompatibleObjectType& obj)\n    {\n        using std::begin;\n        using std::end;\n\n        j.m_type = value_t::object;\n        j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));\n        j.assert_invariant();\n    }\n};\n\n/////////////\n// to_json //\n/////////////\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>\nvoid to_json(BasicJsonType& j, T b) noexcept\n{\n    external_constructor<value_t::boolean>::construct(j, b);\n}\n\ntemplate<typename BasicJsonType, typename CompatibleString,\n         enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const CompatibleString& s)\n{\n    external_constructor<value_t::string>::construct(j, s);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)\n{\n    external_constructor<value_t::string>::construct(j, std::move(s));\n}\n\ntemplate<typename BasicJsonType, typename FloatType,\n         enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, FloatType val) noexcept\n{\n    external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberUnsignedType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept\n{\n    external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleNumberIntegerType,\n         enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept\n{\n    external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));\n}\n\ntemplate<typename BasicJsonType, typename EnumType,\n         enable_if_t<std::is_enum<EnumType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, EnumType e) noexcept\n{\n    using underlying_type = typename std::underlying_type<EnumType>::type;\n    external_constructor<value_t::number_integer>::construct(j, static_cast<underlying_type>(e));\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, const std::vector<bool>& e)\n{\n    external_constructor<value_t::array>::construct(j, e);\n}\n\ntemplate <typename BasicJsonType, typename CompatibleArrayType,\n          enable_if_t<is_compatible_array_type<BasicJsonType,\n                      CompatibleArrayType>::value and\n                      not is_compatible_object_type<\n                          BasicJsonType, CompatibleArrayType>::value and\n                      not is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value and\n                      not is_basic_json<CompatibleArrayType>::value,\n                      int> = 0>\nvoid to_json(BasicJsonType& j, const CompatibleArrayType& arr)\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const std::valarray<T>& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)\n{\n    external_constructor<value_t::array>::construct(j, std::move(arr));\n}\n\ntemplate<typename BasicJsonType, typename CompatibleObjectType,\n         enable_if_t<is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value and not is_basic_json<CompatibleObjectType>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const CompatibleObjectType& obj)\n{\n    external_constructor<value_t::object>::construct(j, obj);\n}\n\ntemplate<typename BasicJsonType>\nvoid to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)\n{\n    external_constructor<value_t::object>::construct(j, std::move(obj));\n}\n\ntemplate <\n    typename BasicJsonType, typename T, std::size_t N,\n    enable_if_t<not std::is_constructible<typename BasicJsonType::string_t,\n                const T (&)[N]>::value,\n                int> = 0 >\nvoid to_json(BasicJsonType& j, const T (&arr)[N])\n{\n    external_constructor<value_t::array>::construct(j, arr);\n}\n\ntemplate<typename BasicJsonType, typename... Args>\nvoid to_json(BasicJsonType& j, const std::pair<Args...>& p)\n{\n    j = {p.first, p.second};\n}\n\n// for https://github.com/nlohmann/json/pull/1134\ntemplate<typename BasicJsonType, typename T,\n         enable_if_t<std::is_same<T, typename iteration_proxy<typename BasicJsonType::iterator>::iteration_proxy_internal>::value, int> = 0>\nvoid to_json(BasicJsonType& j, const T& b)\n{\n    j = {{b.key(), b.value()}};\n}\n\ntemplate<typename BasicJsonType, typename Tuple, std::size_t... Idx>\nvoid to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)\n{\n    j = {std::get<Idx>(t)...};\n}\n\ntemplate<typename BasicJsonType, typename... Args>\nvoid to_json(BasicJsonType& j, const std::tuple<Args...>& t)\n{\n    to_json_tuple_impl(j, t, index_sequence_for<Args...> {});\n}\n\nstruct to_json_fn\n{\n    template<typename BasicJsonType, typename T>\n    auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))\n    -> decltype(to_json(j, std::forward<T>(val)), void())\n    {\n        return to_json(j, std::forward<T>(val));\n    }\n};\n}  // namespace detail\n\n/// namespace to hold default `to_json` function\nnamespace\n{\nconstexpr const auto& to_json = detail::static_const<detail::to_json_fn>::value;\n} // namespace\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n\n#include <cassert> // assert\n#include <cstddef> // size_t\n#include <cstring> // strlen\n#include <istream> // istream\n#include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next\n#include <memory> // shared_ptr, make_shared, addressof\n#include <numeric> // accumulate\n#include <string> // string, char_traits\n#include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer\n#include <utility> // pair, declval\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// the supported input formats\nenum class input_format_t { json, cbor, msgpack, ubjson, bson };\n\n////////////////////\n// input adapters //\n////////////////////\n\n/*!\n@brief abstract input adapter interface\n\nProduces a stream of std::char_traits<char>::int_type characters from a\nstd::istream, a buffer, or some other input type. Accepts the return of\nexactly one non-EOF character for future input. The int_type characters\nreturned consist of all valid char values as positive values (typically\nunsigned char), plus an EOF value outside that range, specified by the value\nof the function std::char_traits<char>::eof(). This value is typically -1, but\ncould be any arbitrary value which is not a valid char value.\n*/\nstruct input_adapter_protocol\n{\n    /// get a character [0,255] or std::char_traits<char>::eof().\n    virtual std::char_traits<char>::int_type get_character() = 0;\n    virtual ~input_adapter_protocol() = default;\n};\n\n/// a type to simplify interfaces\nusing input_adapter_t = std::shared_ptr<input_adapter_protocol>;\n\n/*!\nInput adapter for a (caching) istream. Ignores a UFT Byte Order Mark at\nbeginning of input. Does not support changing the underlying std::streambuf\nin mid-input. Maintains underlying std::istream and std::streambuf to support\nsubsequent use of standard std::istream operations to process any input\ncharacters following those used in parsing the JSON input.  Clears the\nstd::istream flags; any input errors (e.g., EOF) will be detected by the first\nsubsequent call for input from the std::istream.\n*/\nclass input_stream_adapter : public input_adapter_protocol\n{\n  public:\n    ~input_stream_adapter() override\n    {\n        // clear stream flags; we use underlying streambuf I/O, do not\n        // maintain ifstream flags\n        is.clear();\n    }\n\n    explicit input_stream_adapter(std::istream& i)\n        : is(i), sb(*i.rdbuf())\n    {}\n\n    // delete because of pointer members\n    input_stream_adapter(const input_stream_adapter&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&) = delete;\n    input_stream_adapter(input_stream_adapter&&) = delete;\n    input_stream_adapter& operator=(input_stream_adapter&&) = delete;\n\n    // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to\n    // ensure that std::char_traits<char>::eof() and the character 0xFF do not\n    // end up as the same value, eg. 0xFFFFFFFF.\n    std::char_traits<char>::int_type get_character() override\n    {\n        return sb.sbumpc();\n    }\n\n  private:\n    /// the associated input stream\n    std::istream& is;\n    std::streambuf& sb;\n};\n\n/// input adapter for buffer input\nclass input_buffer_adapter : public input_adapter_protocol\n{\n  public:\n    input_buffer_adapter(const char* b, const std::size_t l) noexcept\n        : cursor(b), limit(b + l)\n    {}\n\n    // delete because of pointer members\n    input_buffer_adapter(const input_buffer_adapter&) = delete;\n    input_buffer_adapter& operator=(input_buffer_adapter&) = delete;\n    input_buffer_adapter(input_buffer_adapter&&) = delete;\n    input_buffer_adapter& operator=(input_buffer_adapter&&) = delete;\n    ~input_buffer_adapter() override = default;\n\n    std::char_traits<char>::int_type get_character() noexcept override\n    {\n        if (JSON_LIKELY(cursor < limit))\n        {\n            return std::char_traits<char>::to_int_type(*(cursor++));\n        }\n\n        return std::char_traits<char>::eof();\n    }\n\n  private:\n    /// pointer to the current character\n    const char* cursor;\n    /// pointer past the last character\n    const char* const limit;\n};\n\ntemplate<typename WideStringType, size_t T>\nstruct wide_string_input_helper\n{\n    // UTF-32\n    static void fill_buffer(const WideStringType& str, size_t& current_wchar, std::array<std::char_traits<char>::int_type, 4>& utf8_bytes, size_t& utf8_bytes_index, size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (current_wchar == str.size())\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = static_cast<int>(str[current_wchar++]);\n\n            // UTF-32 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = wc;\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = 0xC0 | ((wc >> 6) & 0x1F);\n                utf8_bytes[1] = 0x80 | (wc & 0x3F);\n                utf8_bytes_filled = 2;\n            }\n            else if (wc <= 0xFFFF)\n            {\n                utf8_bytes[0] = 0xE0 | ((wc >> 12) & 0x0F);\n                utf8_bytes[1] = 0x80 | ((wc >> 6) & 0x3F);\n                utf8_bytes[2] = 0x80 | (wc & 0x3F);\n                utf8_bytes_filled = 3;\n            }\n            else if (wc <= 0x10FFFF)\n            {\n                utf8_bytes[0] = 0xF0 | ((wc >> 18) & 0x07);\n                utf8_bytes[1] = 0x80 | ((wc >> 12) & 0x3F);\n                utf8_bytes[2] = 0x80 | ((wc >> 6) & 0x3F);\n                utf8_bytes[3] = 0x80 | (wc & 0x3F);\n                utf8_bytes_filled = 4;\n            }\n            else\n            {\n                // unknown character\n                utf8_bytes[0] = wc;\n                utf8_bytes_filled = 1;\n            }\n        }\n    }\n};\n\ntemplate<typename WideStringType>\nstruct wide_string_input_helper<WideStringType, 2>\n{\n    // UTF-16\n    static void fill_buffer(const WideStringType& str, size_t& current_wchar, std::array<std::char_traits<char>::int_type, 4>& utf8_bytes, size_t& utf8_bytes_index, size_t& utf8_bytes_filled)\n    {\n        utf8_bytes_index = 0;\n\n        if (current_wchar == str.size())\n        {\n            utf8_bytes[0] = std::char_traits<char>::eof();\n            utf8_bytes_filled = 1;\n        }\n        else\n        {\n            // get the current character\n            const auto wc = static_cast<int>(str[current_wchar++]);\n\n            // UTF-16 to UTF-8 encoding\n            if (wc < 0x80)\n            {\n                utf8_bytes[0] = wc;\n                utf8_bytes_filled = 1;\n            }\n            else if (wc <= 0x7FF)\n            {\n                utf8_bytes[0] = 0xC0 | ((wc >> 6));\n                utf8_bytes[1] = 0x80 | (wc & 0x3F);\n                utf8_bytes_filled = 2;\n            }\n            else if (0xD800 > wc or wc >= 0xE000)\n            {\n                utf8_bytes[0] = 0xE0 | ((wc >> 12));\n                utf8_bytes[1] = 0x80 | ((wc >> 6) & 0x3F);\n                utf8_bytes[2] = 0x80 | (wc & 0x3F);\n                utf8_bytes_filled = 3;\n            }\n            else\n            {\n                if (current_wchar < str.size())\n                {\n                    const auto wc2 = static_cast<int>(str[current_wchar++]);\n                    const int charcode = 0x10000 + (((wc & 0x3FF) << 10) | (wc2 & 0x3FF));\n                    utf8_bytes[0] = 0xf0 | (charcode >> 18);\n                    utf8_bytes[1] = 0x80 | ((charcode >> 12) & 0x3F);\n                    utf8_bytes[2] = 0x80 | ((charcode >> 6) & 0x3F);\n                    utf8_bytes[3] = 0x80 | (charcode & 0x3F);\n                    utf8_bytes_filled = 4;\n                }\n                else\n                {\n                    // unknown character\n                    ++current_wchar;\n                    utf8_bytes[0] = wc;\n                    utf8_bytes_filled = 1;\n                }\n            }\n        }\n    }\n};\n\ntemplate<typename WideStringType>\nclass wide_string_input_adapter : public input_adapter_protocol\n{\n  public:\n    explicit wide_string_input_adapter(const WideStringType& w)  noexcept\n        : str(w)\n    {}\n\n    std::char_traits<char>::int_type get_character() noexcept override\n    {\n        // check if buffer needs to be filled\n        if (utf8_bytes_index == utf8_bytes_filled)\n        {\n            fill_buffer<sizeof(typename WideStringType::value_type)>();\n\n            assert(utf8_bytes_filled > 0);\n            assert(utf8_bytes_index == 0);\n        }\n\n        // use buffer\n        assert(utf8_bytes_filled > 0);\n        assert(utf8_bytes_index < utf8_bytes_filled);\n        return utf8_bytes[utf8_bytes_index++];\n    }\n\n  private:\n    template<size_t T>\n    void fill_buffer()\n    {\n        wide_string_input_helper<WideStringType, T>::fill_buffer(str, current_wchar, utf8_bytes, utf8_bytes_index, utf8_bytes_filled);\n    }\n\n    /// the wstring to process\n    const WideStringType& str;\n\n    /// index of the current wchar in str\n    std::size_t current_wchar = 0;\n\n    /// a buffer for UTF-8 bytes\n    std::array<std::char_traits<char>::int_type, 4> utf8_bytes = {{0, 0, 0, 0}};\n\n    /// index to the utf8_codes array for the next valid byte\n    std::size_t utf8_bytes_index = 0;\n    /// number of valid bytes in the utf8_codes array\n    std::size_t utf8_bytes_filled = 0;\n};\n\nclass input_adapter\n{\n  public:\n    // native support\n\n    /// input adapter for input stream\n    input_adapter(std::istream& i)\n        : ia(std::make_shared<input_stream_adapter>(i)) {}\n\n    /// input adapter for input stream\n    input_adapter(std::istream&& i)\n        : ia(std::make_shared<input_stream_adapter>(i)) {}\n\n    input_adapter(const std::wstring& ws)\n        : ia(std::make_shared<wide_string_input_adapter<std::wstring>>(ws)) {}\n\n    input_adapter(const std::u16string& ws)\n        : ia(std::make_shared<wide_string_input_adapter<std::u16string>>(ws)) {}\n\n    input_adapter(const std::u32string& ws)\n        : ia(std::make_shared<wide_string_input_adapter<std::u32string>>(ws)) {}\n\n    /// input adapter for buffer\n    template<typename CharT,\n             typename std::enable_if<\n                 std::is_pointer<CharT>::value and\n                 std::is_integral<typename std::remove_pointer<CharT>::type>::value and\n                 sizeof(typename std::remove_pointer<CharT>::type) == 1,\n                 int>::type = 0>\n    input_adapter(CharT b, std::size_t l)\n        : ia(std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(b), l)) {}\n\n    // derived support\n\n    /// input adapter for string literal\n    template<typename CharT,\n             typename std::enable_if<\n                 std::is_pointer<CharT>::value and\n                 std::is_integral<typename std::remove_pointer<CharT>::type>::value and\n                 sizeof(typename std::remove_pointer<CharT>::type) == 1,\n                 int>::type = 0>\n    input_adapter(CharT b)\n        : input_adapter(reinterpret_cast<const char*>(b),\n                        std::strlen(reinterpret_cast<const char*>(b))) {}\n\n    /// input adapter for iterator range with contiguous storage\n    template<class IteratorType,\n             typename std::enable_if<\n                 std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,\n                 int>::type = 0>\n    input_adapter(IteratorType first, IteratorType last)\n    {\n#ifndef NDEBUG\n        // assertion to check that the iterator range is indeed contiguous,\n        // see http://stackoverflow.com/a/35008842/266378 for more discussion\n        const auto is_contiguous = std::accumulate(\n                                       first, last, std::pair<bool, int>(true, 0),\n                                       [&first](std::pair<bool, int> res, decltype(*first) val)\n        {\n            res.first &= (val == *(std::next(std::addressof(*first), res.second++)));\n            return res;\n        }).first;\n        assert(is_contiguous);\n#endif\n\n        // assertion to check that each element is 1 byte long\n        static_assert(\n            sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1,\n            \"each element in the iterator range must have the size of 1 byte\");\n\n        const auto len = static_cast<size_t>(std::distance(first, last));\n        if (JSON_LIKELY(len > 0))\n        {\n            // there is at least one element: use the address of first\n            ia = std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(&(*first)), len);\n        }\n        else\n        {\n            // the address of first cannot be used: use nullptr\n            ia = std::make_shared<input_buffer_adapter>(nullptr, len);\n        }\n    }\n\n    /// input adapter for array\n    template<class T, std::size_t N>\n    input_adapter(T (&array)[N])\n        : input_adapter(std::begin(array), std::end(array)) {}\n\n    /// input adapter for contiguous container\n    template<class ContiguousContainer, typename\n             std::enable_if<not std::is_pointer<ContiguousContainer>::value and\n                            std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value,\n                            int>::type = 0>\n    input_adapter(const ContiguousContainer& c)\n        : input_adapter(std::begin(c), std::end(c)) {}\n\n    operator input_adapter_t()\n    {\n        return ia;\n    }\n\n  private:\n    /// the actual adapter\n    input_adapter_t ia = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n\n#include <clocale> // localeconv\n#include <cstddef> // size_t\n#include <cstdlib> // strtof, strtod, strtold, strtoll, strtoull\n#include <cstdio> // snprintf\n#include <initializer_list> // initializer_list\n#include <string> // char_traits, string\n#include <vector> // vector\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/position_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////\n// lexer //\n///////////\n\n/*!\n@brief lexical analysis\n\nThis class organizes the lexical analysis during JSON deserialization.\n*/\ntemplate<typename BasicJsonType>\nclass lexer\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n\n  public:\n    /// token types for the parser\n    enum class token_type\n    {\n        uninitialized,    ///< indicating the scanner is uninitialized\n        literal_true,     ///< the `true` literal\n        literal_false,    ///< the `false` literal\n        literal_null,     ///< the `null` literal\n        value_string,     ///< a string -- use get_string() for actual value\n        value_unsigned,   ///< an unsigned integer -- use get_number_unsigned() for actual value\n        value_integer,    ///< a signed integer -- use get_number_integer() for actual value\n        value_float,      ///< an floating point number -- use get_number_float() for actual value\n        begin_array,      ///< the character for array begin `[`\n        begin_object,     ///< the character for object begin `{`\n        end_array,        ///< the character for array end `]`\n        end_object,       ///< the character for object end `}`\n        name_separator,   ///< the name separator `:`\n        value_separator,  ///< the value separator `,`\n        parse_error,      ///< indicating a parse error\n        end_of_input,     ///< indicating the end of the input buffer\n        literal_or_value  ///< a literal or the begin of a value (only for diagnostics)\n    };\n\n    /// return name of values of type token_type (only used for errors)\n    static const char* token_type_name(const token_type t) noexcept\n    {\n        switch (t)\n        {\n            case token_type::uninitialized:\n                return \"<uninitialized>\";\n            case token_type::literal_true:\n                return \"true literal\";\n            case token_type::literal_false:\n                return \"false literal\";\n            case token_type::literal_null:\n                return \"null literal\";\n            case token_type::value_string:\n                return \"string literal\";\n            case lexer::token_type::value_unsigned:\n            case lexer::token_type::value_integer:\n            case lexer::token_type::value_float:\n                return \"number literal\";\n            case token_type::begin_array:\n                return \"'['\";\n            case token_type::begin_object:\n                return \"'{'\";\n            case token_type::end_array:\n                return \"']'\";\n            case token_type::end_object:\n                return \"'}'\";\n            case token_type::name_separator:\n                return \"':'\";\n            case token_type::value_separator:\n                return \"','\";\n            case token_type::parse_error:\n                return \"<parse error>\";\n            case token_type::end_of_input:\n                return \"end of input\";\n            case token_type::literal_or_value:\n                return \"'[', '{', or a literal\";\n            // LCOV_EXCL_START\n            default: // catch non-enum values\n                return \"unknown token\";\n                // LCOV_EXCL_STOP\n        }\n    }\n\n    explicit lexer(detail::input_adapter_t&& adapter)\n        : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {}\n\n    // delete because of pointer members\n    lexer(const lexer&) = delete;\n    lexer(lexer&&) = delete;\n    lexer& operator=(lexer&) = delete;\n    lexer& operator=(lexer&&) = delete;\n    ~lexer() = default;\n\n  private:\n    /////////////////////\n    // locales\n    /////////////////////\n\n    /// return the locale-dependent decimal point\n    static char get_decimal_point() noexcept\n    {\n        const auto loc = localeconv();\n        assert(loc != nullptr);\n        return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point);\n    }\n\n    /////////////////////\n    // scan functions\n    /////////////////////\n\n    /*!\n    @brief get codepoint from 4 hex characters following `\\u`\n\n    For input \"\\u c1 c2 c3 c4\" the codepoint is:\n      (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4\n    = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0)\n\n    Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f'\n    must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The\n    conversion is done by subtracting the offset (0x30, 0x37, and 0x57)\n    between the ASCII value of the character and the desired integer value.\n\n    @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or\n            non-hex character)\n    */\n    int get_codepoint()\n    {\n        // this function only makes sense after reading `\\u`\n        assert(current == 'u');\n        int codepoint = 0;\n\n        const auto factors = { 12, 8, 4, 0 };\n        for (const auto factor : factors)\n        {\n            get();\n\n            if (current >= '0' and current <= '9')\n            {\n                codepoint += ((current - 0x30) << factor);\n            }\n            else if (current >= 'A' and current <= 'F')\n            {\n                codepoint += ((current - 0x37) << factor);\n            }\n            else if (current >= 'a' and current <= 'f')\n            {\n                codepoint += ((current - 0x57) << factor);\n            }\n            else\n            {\n                return -1;\n            }\n        }\n\n        assert(0x0000 <= codepoint and codepoint <= 0xFFFF);\n        return codepoint;\n    }\n\n    /*!\n    @brief check if the next byte(s) are inside a given range\n\n    Adds the current byte and, for each passed range, reads a new byte and\n    checks if it is inside the range. If a violation was detected, set up an\n    error message and return false. Otherwise, return true.\n\n    @param[in] ranges  list of integers; interpreted as list of pairs of\n                       inclusive lower and upper bound, respectively\n\n    @pre The passed list @a ranges must have 2, 4, or 6 elements; that is,\n         1, 2, or 3 pairs. This precondition is enforced by an assertion.\n\n    @return true if and only if no range violation was detected\n    */\n    bool next_byte_in_range(std::initializer_list<int> ranges)\n    {\n        assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6);\n        add(current);\n\n        for (auto range = ranges.begin(); range != ranges.end(); ++range)\n        {\n            get();\n            if (JSON_LIKELY(*range <= current and current <= *(++range)))\n            {\n                add(current);\n            }\n            else\n            {\n                error_message = \"invalid string: ill-formed UTF-8 byte\";\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief scan a string literal\n\n    This function scans a string according to Sect. 7 of RFC 7159. While\n    scanning, bytes are escaped and copied into buffer token_buffer. Then the\n    function returns successfully, token_buffer is *not* null-terminated (as it\n    may contain \\0 bytes), and token_buffer.size() is the number of bytes in the\n    string.\n\n    @return token_type::value_string if string could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note In case of errors, variable error_message contains a textual\n          description.\n    */\n    token_type scan_string()\n    {\n        // reset token_buffer (ignore opening quote)\n        reset();\n\n        // we entered the function by reading an open quote\n        assert(current == '\\\"');\n\n        while (true)\n        {\n            // get next character\n            switch (get())\n            {\n                // end of file while parsing string\n                case std::char_traits<char>::eof():\n                {\n                    error_message = \"invalid string: missing closing quote\";\n                    return token_type::parse_error;\n                }\n\n                // closing quote\n                case '\\\"':\n                {\n                    return token_type::value_string;\n                }\n\n                // escapes\n                case '\\\\':\n                {\n                    switch (get())\n                    {\n                        // quotation mark\n                        case '\\\"':\n                            add('\\\"');\n                            break;\n                        // reverse solidus\n                        case '\\\\':\n                            add('\\\\');\n                            break;\n                        // solidus\n                        case '/':\n                            add('/');\n                            break;\n                        // backspace\n                        case 'b':\n                            add('\\b');\n                            break;\n                        // form feed\n                        case 'f':\n                            add('\\f');\n                            break;\n                        // line feed\n                        case 'n':\n                            add('\\n');\n                            break;\n                        // carriage return\n                        case 'r':\n                            add('\\r');\n                            break;\n                        // tab\n                        case 't':\n                            add('\\t');\n                            break;\n\n                        // unicode escapes\n                        case 'u':\n                        {\n                            const int codepoint1 = get_codepoint();\n                            int codepoint = codepoint1; // start with codepoint1\n\n                            if (JSON_UNLIKELY(codepoint1 == -1))\n                            {\n                                error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                return token_type::parse_error;\n                            }\n\n                            // check if code point is a high surrogate\n                            if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF)\n                            {\n                                // expect next \\uxxxx entry\n                                if (JSON_LIKELY(get() == '\\\\' and get() == 'u'))\n                                {\n                                    const int codepoint2 = get_codepoint();\n\n                                    if (JSON_UNLIKELY(codepoint2 == -1))\n                                    {\n                                        error_message = \"invalid string: '\\\\u' must be followed by 4 hex digits\";\n                                        return token_type::parse_error;\n                                    }\n\n                                    // check if codepoint2 is a low surrogate\n                                    if (JSON_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF))\n                                    {\n                                        // overwrite codepoint\n                                        codepoint =\n                                            // high surrogate occupies the most significant 22 bits\n                                            (codepoint1 << 10)\n                                            // low surrogate occupies the least significant 15 bits\n                                            + codepoint2\n                                            // there is still the 0xD800, 0xDC00 and 0x10000 noise\n                                            // in the result so we have to subtract with:\n                                            // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00\n                                            - 0x35FDC00;\n                                    }\n                                    else\n                                    {\n                                        error_message = \"invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF\";\n                                        return token_type::parse_error;\n                                    }\n                                }\n                                else\n                                {\n                                    error_message = \"invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n                            else\n                            {\n                                if (JSON_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF))\n                                {\n                                    error_message = \"invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF\";\n                                    return token_type::parse_error;\n                                }\n                            }\n\n                            // result of the above calculation yields a proper codepoint\n                            assert(0x00 <= codepoint and codepoint <= 0x10FFFF);\n\n                            // translate codepoint into bytes\n                            if (codepoint < 0x80)\n                            {\n                                // 1-byte characters: 0xxxxxxx (ASCII)\n                                add(codepoint);\n                            }\n                            else if (codepoint <= 0x7FF)\n                            {\n                                // 2-byte characters: 110xxxxx 10xxxxxx\n                                add(0xC0 | (codepoint >> 6));\n                                add(0x80 | (codepoint & 0x3F));\n                            }\n                            else if (codepoint <= 0xFFFF)\n                            {\n                                // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx\n                                add(0xE0 | (codepoint >> 12));\n                                add(0x80 | ((codepoint >> 6) & 0x3F));\n                                add(0x80 | (codepoint & 0x3F));\n                            }\n                            else\n                            {\n                                // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx\n                                add(0xF0 | (codepoint >> 18));\n                                add(0x80 | ((codepoint >> 12) & 0x3F));\n                                add(0x80 | ((codepoint >> 6) & 0x3F));\n                                add(0x80 | (codepoint & 0x3F));\n                            }\n\n                            break;\n                        }\n\n                        // other characters after escape\n                        default:\n                            error_message = \"invalid string: forbidden character after backslash\";\n                            return token_type::parse_error;\n                    }\n\n                    break;\n                }\n\n                // invalid control characters\n                case 0x00:\n                {\n                    error_message = \"invalid string: control character U+0000 (NUL) must be escaped to \\\\u0000\";\n                    return token_type::parse_error;\n                }\n\n                case 0x01:\n                {\n                    error_message = \"invalid string: control character U+0001 (SOH) must be escaped to \\\\u0001\";\n                    return token_type::parse_error;\n                }\n\n                case 0x02:\n                {\n                    error_message = \"invalid string: control character U+0002 (STX) must be escaped to \\\\u0002\";\n                    return token_type::parse_error;\n                }\n\n                case 0x03:\n                {\n                    error_message = \"invalid string: control character U+0003 (ETX) must be escaped to \\\\u0003\";\n                    return token_type::parse_error;\n                }\n\n                case 0x04:\n                {\n                    error_message = \"invalid string: control character U+0004 (EOT) must be escaped to \\\\u0004\";\n                    return token_type::parse_error;\n                }\n\n                case 0x05:\n                {\n                    error_message = \"invalid string: control character U+0005 (ENQ) must be escaped to \\\\u0005\";\n                    return token_type::parse_error;\n                }\n\n                case 0x06:\n                {\n                    error_message = \"invalid string: control character U+0006 (ACK) must be escaped to \\\\u0006\";\n                    return token_type::parse_error;\n                }\n\n                case 0x07:\n                {\n                    error_message = \"invalid string: control character U+0007 (BEL) must be escaped to \\\\u0007\";\n                    return token_type::parse_error;\n                }\n\n                case 0x08:\n                {\n                    error_message = \"invalid string: control character U+0008 (BS) must be escaped to \\\\u0008 or \\\\b\";\n                    return token_type::parse_error;\n                }\n\n                case 0x09:\n                {\n                    error_message = \"invalid string: control character U+0009 (HT) must be escaped to \\\\u0009 or \\\\t\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0A:\n                {\n                    error_message = \"invalid string: control character U+000A (LF) must be escaped to \\\\u000A or \\\\n\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0B:\n                {\n                    error_message = \"invalid string: control character U+000B (VT) must be escaped to \\\\u000B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0C:\n                {\n                    error_message = \"invalid string: control character U+000C (FF) must be escaped to \\\\u000C or \\\\f\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0D:\n                {\n                    error_message = \"invalid string: control character U+000D (CR) must be escaped to \\\\u000D or \\\\r\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0E:\n                {\n                    error_message = \"invalid string: control character U+000E (SO) must be escaped to \\\\u000E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x0F:\n                {\n                    error_message = \"invalid string: control character U+000F (SI) must be escaped to \\\\u000F\";\n                    return token_type::parse_error;\n                }\n\n                case 0x10:\n                {\n                    error_message = \"invalid string: control character U+0010 (DLE) must be escaped to \\\\u0010\";\n                    return token_type::parse_error;\n                }\n\n                case 0x11:\n                {\n                    error_message = \"invalid string: control character U+0011 (DC1) must be escaped to \\\\u0011\";\n                    return token_type::parse_error;\n                }\n\n                case 0x12:\n                {\n                    error_message = \"invalid string: control character U+0012 (DC2) must be escaped to \\\\u0012\";\n                    return token_type::parse_error;\n                }\n\n                case 0x13:\n                {\n                    error_message = \"invalid string: control character U+0013 (DC3) must be escaped to \\\\u0013\";\n                    return token_type::parse_error;\n                }\n\n                case 0x14:\n                {\n                    error_message = \"invalid string: control character U+0014 (DC4) must be escaped to \\\\u0014\";\n                    return token_type::parse_error;\n                }\n\n                case 0x15:\n                {\n                    error_message = \"invalid string: control character U+0015 (NAK) must be escaped to \\\\u0015\";\n                    return token_type::parse_error;\n                }\n\n                case 0x16:\n                {\n                    error_message = \"invalid string: control character U+0016 (SYN) must be escaped to \\\\u0016\";\n                    return token_type::parse_error;\n                }\n\n                case 0x17:\n                {\n                    error_message = \"invalid string: control character U+0017 (ETB) must be escaped to \\\\u0017\";\n                    return token_type::parse_error;\n                }\n\n                case 0x18:\n                {\n                    error_message = \"invalid string: control character U+0018 (CAN) must be escaped to \\\\u0018\";\n                    return token_type::parse_error;\n                }\n\n                case 0x19:\n                {\n                    error_message = \"invalid string: control character U+0019 (EM) must be escaped to \\\\u0019\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1A:\n                {\n                    error_message = \"invalid string: control character U+001A (SUB) must be escaped to \\\\u001A\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1B:\n                {\n                    error_message = \"invalid string: control character U+001B (ESC) must be escaped to \\\\u001B\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1C:\n                {\n                    error_message = \"invalid string: control character U+001C (FS) must be escaped to \\\\u001C\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1D:\n                {\n                    error_message = \"invalid string: control character U+001D (GS) must be escaped to \\\\u001D\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1E:\n                {\n                    error_message = \"invalid string: control character U+001E (RS) must be escaped to \\\\u001E\";\n                    return token_type::parse_error;\n                }\n\n                case 0x1F:\n                {\n                    error_message = \"invalid string: control character U+001F (US) must be escaped to \\\\u001F\";\n                    return token_type::parse_error;\n                }\n\n                // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace))\n                case 0x20:\n                case 0x21:\n                case 0x23:\n                case 0x24:\n                case 0x25:\n                case 0x26:\n                case 0x27:\n                case 0x28:\n                case 0x29:\n                case 0x2A:\n                case 0x2B:\n                case 0x2C:\n                case 0x2D:\n                case 0x2E:\n                case 0x2F:\n                case 0x30:\n                case 0x31:\n                case 0x32:\n                case 0x33:\n                case 0x34:\n                case 0x35:\n                case 0x36:\n                case 0x37:\n                case 0x38:\n                case 0x39:\n                case 0x3A:\n                case 0x3B:\n                case 0x3C:\n                case 0x3D:\n                case 0x3E:\n                case 0x3F:\n                case 0x40:\n                case 0x41:\n                case 0x42:\n                case 0x43:\n                case 0x44:\n                case 0x45:\n                case 0x46:\n                case 0x47:\n                case 0x48:\n                case 0x49:\n                case 0x4A:\n                case 0x4B:\n                case 0x4C:\n                case 0x4D:\n                case 0x4E:\n                case 0x4F:\n                case 0x50:\n                case 0x51:\n                case 0x52:\n                case 0x53:\n                case 0x54:\n                case 0x55:\n                case 0x56:\n                case 0x57:\n                case 0x58:\n                case 0x59:\n                case 0x5A:\n                case 0x5B:\n                case 0x5D:\n                case 0x5E:\n                case 0x5F:\n                case 0x60:\n                case 0x61:\n                case 0x62:\n                case 0x63:\n                case 0x64:\n                case 0x65:\n                case 0x66:\n                case 0x67:\n                case 0x68:\n                case 0x69:\n                case 0x6A:\n                case 0x6B:\n                case 0x6C:\n                case 0x6D:\n                case 0x6E:\n                case 0x6F:\n                case 0x70:\n                case 0x71:\n                case 0x72:\n                case 0x73:\n                case 0x74:\n                case 0x75:\n                case 0x76:\n                case 0x77:\n                case 0x78:\n                case 0x79:\n                case 0x7A:\n                case 0x7B:\n                case 0x7C:\n                case 0x7D:\n                case 0x7E:\n                case 0x7F:\n                {\n                    add(current);\n                    break;\n                }\n\n                // U+0080..U+07FF: bytes C2..DF 80..BF\n                case 0xC2:\n                case 0xC3:\n                case 0xC4:\n                case 0xC5:\n                case 0xC6:\n                case 0xC7:\n                case 0xC8:\n                case 0xC9:\n                case 0xCA:\n                case 0xCB:\n                case 0xCC:\n                case 0xCD:\n                case 0xCE:\n                case 0xCF:\n                case 0xD0:\n                case 0xD1:\n                case 0xD2:\n                case 0xD3:\n                case 0xD4:\n                case 0xD5:\n                case 0xD6:\n                case 0xD7:\n                case 0xD8:\n                case 0xD9:\n                case 0xDA:\n                case 0xDB:\n                case 0xDC:\n                case 0xDD:\n                case 0xDE:\n                case 0xDF:\n                {\n                    if (JSON_UNLIKELY(not next_byte_in_range({0x80, 0xBF})))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+0800..U+0FFF: bytes E0 A0..BF 80..BF\n                case 0xE0:\n                {\n                    if (JSON_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF\n                // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF\n                case 0xE1:\n                case 0xE2:\n                case 0xE3:\n                case 0xE4:\n                case 0xE5:\n                case 0xE6:\n                case 0xE7:\n                case 0xE8:\n                case 0xE9:\n                case 0xEA:\n                case 0xEB:\n                case 0xEC:\n                case 0xEE:\n                case 0xEF:\n                {\n                    if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+D000..U+D7FF: bytes ED 80..9F 80..BF\n                case 0xED:\n                {\n                    if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF\n                case 0xF0:\n                {\n                    if (JSON_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF\n                case 0xF1:\n                case 0xF2:\n                case 0xF3:\n                {\n                    if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF\n                case 0xF4:\n                {\n                    if (JSON_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF}))))\n                    {\n                        return token_type::parse_error;\n                    }\n                    break;\n                }\n\n                // remaining bytes (80..C1 and F5..FF) are ill-formed\n                default:\n                {\n                    error_message = \"invalid string: ill-formed UTF-8 byte\";\n                    return token_type::parse_error;\n                }\n            }\n        }\n    }\n\n    static void strtof(float& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtof(str, endptr);\n    }\n\n    static void strtof(double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtod(str, endptr);\n    }\n\n    static void strtof(long double& f, const char* str, char** endptr) noexcept\n    {\n        f = std::strtold(str, endptr);\n    }\n\n    /*!\n    @brief scan a number literal\n\n    This function scans a string according to Sect. 6 of RFC 7159.\n\n    The function is realized with a deterministic finite state machine derived\n    from the grammar described in RFC 7159. Starting in state \"init\", the\n    input is read and used to determined the next state. Only state \"done\"\n    accepts the number. State \"error\" is a trap state to model errors. In the\n    table below, \"anything\" means any character but the ones listed before.\n\n    state    | 0        | 1-9      | e E      | +       | -       | .        | anything\n    ---------|----------|----------|----------|---------|---------|----------|-----------\n    init     | zero     | any1     | [error]  | [error] | minus   | [error]  | [error]\n    minus    | zero     | any1     | [error]  | [error] | [error] | [error]  | [error]\n    zero     | done     | done     | exponent | done    | done    | decimal1 | done\n    any1     | any1     | any1     | exponent | done    | done    | decimal1 | done\n    decimal1 | decimal2 | [error]  | [error]  | [error] | [error] | [error]  | [error]\n    decimal2 | decimal2 | decimal2 | exponent | done    | done    | done     | done\n    exponent | any2     | any2     | [error]  | sign    | sign    | [error]  | [error]\n    sign     | any2     | any2     | [error]  | [error] | [error] | [error]  | [error]\n    any2     | any2     | any2     | done     | done    | done    | done     | done\n\n    The state machine is realized with one label per state (prefixed with\n    \"scan_number_\") and `goto` statements between them. The state machine\n    contains cycles, but any cycle can be left when EOF is read. Therefore,\n    the function is guaranteed to terminate.\n\n    During scanning, the read bytes are stored in token_buffer. This string is\n    then converted to a signed integer, an unsigned integer, or a\n    floating-point number.\n\n    @return token_type::value_unsigned, token_type::value_integer, or\n            token_type::value_float if number could be successfully scanned,\n            token_type::parse_error otherwise\n\n    @note The scanner is independent of the current locale. Internally, the\n          locale's decimal point is used instead of `.` to work with the\n          locale-dependent converters.\n    */\n    token_type scan_number()  // lgtm [cpp/use-of-goto]\n    {\n        // reset token_buffer to store the number's bytes\n        reset();\n\n        // the type of the parsed number; initially set to unsigned; will be\n        // changed if minus sign, decimal point or exponent is read\n        token_type number_type = token_type::value_unsigned;\n\n        // state (init): we just found out we need to scan a number\n        switch (current)\n        {\n            case '-':\n            {\n                add(current);\n                goto scan_number_minus;\n            }\n\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            // LCOV_EXCL_START\n            default:\n            {\n                // all other characters are rejected outside scan_number()\n                assert(false);\n            }\n                // LCOV_EXCL_STOP\n        }\n\nscan_number_minus:\n        // state: we just parsed a leading minus sign\n        number_type = token_type::value_integer;\n        switch (get())\n        {\n            case '0':\n            {\n                add(current);\n                goto scan_number_zero;\n            }\n\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '-'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_zero:\n        // state: we just parse a zero (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '.':\n            {\n                add(decimal_point_char);\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_any1:\n        // state: we just parsed a number 0-9 (maybe with a leading minus sign)\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any1;\n            }\n\n            case '.':\n            {\n                add(decimal_point_char);\n                goto scan_number_decimal1;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_decimal1:\n        // state: we just parsed a decimal point\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after '.'\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_decimal2:\n        // we just parsed at least one number after a decimal point\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_decimal2;\n            }\n\n            case 'e':\n            case 'E':\n            {\n                add(current);\n                goto scan_number_exponent;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_exponent:\n        // we just parsed an exponent\n        number_type = token_type::value_float;\n        switch (get())\n        {\n            case '+':\n            case '-':\n            {\n                add(current);\n                goto scan_number_sign;\n            }\n\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message =\n                    \"invalid number; expected '+', '-', or digit after exponent\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_sign:\n        // we just parsed an exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n            {\n                error_message = \"invalid number; expected digit after exponent sign\";\n                return token_type::parse_error;\n            }\n        }\n\nscan_number_any2:\n        // we just parsed a number after the exponent or exponent sign\n        switch (get())\n        {\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n            {\n                add(current);\n                goto scan_number_any2;\n            }\n\n            default:\n                goto scan_number_done;\n        }\n\nscan_number_done:\n        // unget the character after the number (we only read it to know that\n        // we are done scanning a number)\n        unget();\n\n        char* endptr = nullptr;\n        errno = 0;\n\n        // try to parse integers first and fall back to floats\n        if (number_type == token_type::value_unsigned)\n        {\n            const auto x = std::strtoull(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            assert(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno == 0)\n            {\n                value_unsigned = static_cast<number_unsigned_t>(x);\n                if (value_unsigned == x)\n                {\n                    return token_type::value_unsigned;\n                }\n            }\n        }\n        else if (number_type == token_type::value_integer)\n        {\n            const auto x = std::strtoll(token_buffer.data(), &endptr, 10);\n\n            // we checked the number format before\n            assert(endptr == token_buffer.data() + token_buffer.size());\n\n            if (errno == 0)\n            {\n                value_integer = static_cast<number_integer_t>(x);\n                if (value_integer == x)\n                {\n                    return token_type::value_integer;\n                }\n            }\n        }\n\n        // this code is reached if we parse a floating-point number or if an\n        // integer conversion above failed\n        strtof(value_float, token_buffer.data(), &endptr);\n\n        // we checked the number format before\n        assert(endptr == token_buffer.data() + token_buffer.size());\n\n        return token_type::value_float;\n    }\n\n    /*!\n    @param[in] literal_text  the literal text to expect\n    @param[in] length        the length of the passed literal text\n    @param[in] return_type   the token type to return on success\n    */\n    token_type scan_literal(const char* literal_text, const std::size_t length,\n                            token_type return_type)\n    {\n        assert(current == literal_text[0]);\n        for (std::size_t i = 1; i < length; ++i)\n        {\n            if (JSON_UNLIKELY(get() != literal_text[i]))\n            {\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n            }\n        }\n        return return_type;\n    }\n\n    /////////////////////\n    // input management\n    /////////////////////\n\n    /// reset token_buffer; current character is beginning of token\n    void reset() noexcept\n    {\n        token_buffer.clear();\n        token_string.clear();\n        token_string.push_back(std::char_traits<char>::to_char_type(current));\n    }\n\n    /*\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a\n    `std::char_traits<char>::eof()` in that case.  Stores the scanned characters\n    for use in error messages.\n\n    @return character read from the input\n    */\n    std::char_traits<char>::int_type get()\n    {\n        ++position.chars_read_total;\n        ++position.chars_read_current_line;\n\n        if (next_unget)\n        {\n            // just reset the next_unget variable and work with current\n            next_unget = false;\n        }\n        else\n        {\n            current = ia->get_character();\n        }\n\n        if (JSON_LIKELY(current != std::char_traits<char>::eof()))\n        {\n            token_string.push_back(std::char_traits<char>::to_char_type(current));\n        }\n\n        if (current == '\\n')\n        {\n            ++position.lines_read;\n            ++position.chars_read_current_line = 0;\n        }\n\n        return current;\n    }\n\n    /*!\n    @brief unget current character (read it again on next get)\n\n    We implement unget by setting variable next_unget to true. The input is not\n    changed - we just simulate ungetting by modifying chars_read_total,\n    chars_read_current_line, and token_string. The next call to get() will\n    behave as if the unget character is read again.\n    */\n    void unget()\n    {\n        next_unget = true;\n\n        --position.chars_read_total;\n\n        // in case we \"unget\" a newline, we have to also decrement the lines_read\n        if (position.chars_read_current_line == 0)\n        {\n            if (position.lines_read > 0)\n            {\n                --position.lines_read;\n            }\n        }\n        else\n        {\n            --position.chars_read_current_line;\n        }\n\n        if (JSON_LIKELY(current != std::char_traits<char>::eof()))\n        {\n            assert(token_string.size() != 0);\n            token_string.pop_back();\n        }\n    }\n\n    /// add a character to token_buffer\n    void add(int c)\n    {\n        token_buffer.push_back(std::char_traits<char>::to_char_type(c));\n    }\n\n  public:\n    /////////////////////\n    // value getters\n    /////////////////////\n\n    /// return integer value\n    constexpr number_integer_t get_number_integer() const noexcept\n    {\n        return value_integer;\n    }\n\n    /// return unsigned integer value\n    constexpr number_unsigned_t get_number_unsigned() const noexcept\n    {\n        return value_unsigned;\n    }\n\n    /// return floating-point value\n    constexpr number_float_t get_number_float() const noexcept\n    {\n        return value_float;\n    }\n\n    /// return current string value (implicitly resets the token; useful only once)\n    string_t& get_string()\n    {\n        return token_buffer;\n    }\n\n    /////////////////////\n    // diagnostics\n    /////////////////////\n\n    /// return position of last read token\n    constexpr position_t get_position() const noexcept\n    {\n        return position;\n    }\n\n    /// return the last read token (for errors only).  Will never contain EOF\n    /// (an arbitrary value that is not a valid char value, often -1), because\n    /// 255 may legitimately occur.  May contain NUL, which should be escaped.\n    std::string get_token_string() const\n    {\n        // escape control characters\n        std::string result;\n        for (const auto c : token_string)\n        {\n            if ('\\x00' <= c and c <= '\\x1F')\n            {\n                // escape control characters\n                char cs[9];\n                snprintf(cs, 9, \"<U+%.4X>\", static_cast<unsigned char>(c));\n                result += cs;\n            }\n            else\n            {\n                // add character as is\n                result.push_back(c);\n            }\n        }\n\n        return result;\n    }\n\n    /// return syntax error message\n    constexpr const char* get_error_message() const noexcept\n    {\n        return error_message;\n    }\n\n    /////////////////////\n    // actual scanner\n    /////////////////////\n\n    /*!\n    @brief skip the UTF-8 byte order mark\n    @return true iff there is no BOM or the correct BOM has been skipped\n    */\n    bool skip_bom()\n    {\n        if (get() == 0xEF)\n        {\n            // check if we completely parse the BOM\n            return get() == 0xBB and get() == 0xBF;\n        }\n\n        // the first character is not the beginning of the BOM; unget it to\n        // process is later\n        unget();\n        return true;\n    }\n\n    token_type scan()\n    {\n        // initially, skip the BOM\n        if (position.chars_read_total == 0 and not skip_bom())\n        {\n            error_message = \"invalid BOM; must be 0xEF 0xBB 0xBF if given\";\n            return token_type::parse_error;\n        }\n\n        // read next character and ignore whitespace\n        do\n        {\n            get();\n        }\n        while (current == ' ' or current == '\\t' or current == '\\n' or current == '\\r');\n\n        switch (current)\n        {\n            // structural characters\n            case '[':\n                return token_type::begin_array;\n            case ']':\n                return token_type::end_array;\n            case '{':\n                return token_type::begin_object;\n            case '}':\n                return token_type::end_object;\n            case ':':\n                return token_type::name_separator;\n            case ',':\n                return token_type::value_separator;\n\n            // literals\n            case 't':\n                return scan_literal(\"true\", 4, token_type::literal_true);\n            case 'f':\n                return scan_literal(\"false\", 5, token_type::literal_false);\n            case 'n':\n                return scan_literal(\"null\", 4, token_type::literal_null);\n\n            // string\n            case '\\\"':\n                return scan_string();\n\n            // number\n            case '-':\n            case '0':\n            case '1':\n            case '2':\n            case '3':\n            case '4':\n            case '5':\n            case '6':\n            case '7':\n            case '8':\n            case '9':\n                return scan_number();\n\n            // end of input (the null byte is needed when parsing from\n            // string literals)\n            case '\\0':\n            case std::char_traits<char>::eof():\n                return token_type::end_of_input;\n\n            // error\n            default:\n                error_message = \"invalid literal\";\n                return token_type::parse_error;\n        }\n    }\n\n  private:\n    /// input adapter\n    detail::input_adapter_t ia = nullptr;\n\n    /// the current character\n    std::char_traits<char>::int_type current = std::char_traits<char>::eof();\n\n    /// whether the next get() call should just return current\n    bool next_unget = false;\n\n    /// the start position of the current token\n    position_t position;\n\n    /// raw input token string (for error messages)\n    std::vector<char> token_string {};\n\n    /// buffer for variable-length tokens (numbers, strings)\n    string_t token_buffer {};\n\n    /// a description of occurred lexer errors\n    const char* error_message = \"\";\n\n    // number values\n    number_integer_t value_integer = 0;\n    number_unsigned_t value_unsigned = 0;\n    number_float_t value_float = 0;\n\n    /// the decimal point\n    const char decimal_point_char = '.';\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/parser.hpp>\n\n\n#include <cassert> // assert\n#include <cmath> // isfinite\n#include <cstdint> // uint8_t\n#include <functional> // function\n#include <string> // string\n#include <utility> // move\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/is_sax.hpp>\n\n\n#include <cstdint> // size_t\n#include <utility> // declval\n\n// #include <nlohmann/detail/meta/detected.hpp>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate <typename T>\nusing null_function_t = decltype(std::declval<T&>().null());\n\ntemplate <typename T>\nusing boolean_function_t =\n    decltype(std::declval<T&>().boolean(std::declval<bool>()));\n\ntemplate <typename T, typename Integer>\nusing number_integer_function_t =\n    decltype(std::declval<T&>().number_integer(std::declval<Integer>()));\n\ntemplate <typename T, typename Unsigned>\nusing number_unsigned_function_t =\n    decltype(std::declval<T&>().number_unsigned(std::declval<Unsigned>()));\n\ntemplate <typename T, typename Float, typename String>\nusing number_float_function_t = decltype(std::declval<T&>().number_float(\n                                    std::declval<Float>(), std::declval<const String&>()));\n\ntemplate <typename T, typename String>\nusing string_function_t =\n    decltype(std::declval<T&>().string(std::declval<String&>()));\n\ntemplate <typename T>\nusing start_object_function_t =\n    decltype(std::declval<T&>().start_object(std::declval<std::size_t>()));\n\ntemplate <typename T, typename String>\nusing key_function_t =\n    decltype(std::declval<T&>().key(std::declval<String&>()));\n\ntemplate <typename T>\nusing end_object_function_t = decltype(std::declval<T&>().end_object());\n\ntemplate <typename T>\nusing start_array_function_t =\n    decltype(std::declval<T&>().start_array(std::declval<std::size_t>()));\n\ntemplate <typename T>\nusing end_array_function_t = decltype(std::declval<T&>().end_array());\n\ntemplate <typename T, typename Exception>\nusing parse_error_function_t = decltype(std::declval<T&>().parse_error(\n        std::declval<std::size_t>(), std::declval<const std::string&>(),\n        std::declval<const Exception&>()));\n\ntemplate <typename SAX, typename BasicJsonType>\nstruct is_sax\n{\n  private:\n    static_assert(is_basic_json<BasicJsonType>::value,\n                  \"BasicJsonType must be of type basic_json<...>\");\n\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using exception_t = typename BasicJsonType::exception;\n\n  public:\n    static constexpr bool value =\n        is_detected_exact<bool, null_function_t, SAX>::value &&\n        is_detected_exact<bool, boolean_function_t, SAX>::value &&\n        is_detected_exact<bool, number_integer_function_t, SAX,\n        number_integer_t>::value &&\n        is_detected_exact<bool, number_unsigned_function_t, SAX,\n        number_unsigned_t>::value &&\n        is_detected_exact<bool, number_float_function_t, SAX, number_float_t,\n        string_t>::value &&\n        is_detected_exact<bool, string_function_t, SAX, string_t>::value &&\n        is_detected_exact<bool, start_object_function_t, SAX>::value &&\n        is_detected_exact<bool, key_function_t, SAX, string_t>::value &&\n        is_detected_exact<bool, end_object_function_t, SAX>::value &&\n        is_detected_exact<bool, start_array_function_t, SAX>::value &&\n        is_detected_exact<bool, end_array_function_t, SAX>::value &&\n        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value;\n};\n\ntemplate <typename SAX, typename BasicJsonType>\nstruct is_sax_static_asserts\n{\n  private:\n    static_assert(is_basic_json<BasicJsonType>::value,\n                  \"BasicJsonType must be of type basic_json<...>\");\n\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using exception_t = typename BasicJsonType::exception;\n\n  public:\n    static_assert(is_detected_exact<bool, null_function_t, SAX>::value,\n                  \"Missing/invalid function: bool null()\");\n    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,\n                  \"Missing/invalid function: bool boolean(bool)\");\n    static_assert(is_detected_exact<bool, boolean_function_t, SAX>::value,\n                  \"Missing/invalid function: bool boolean(bool)\");\n    static_assert(\n        is_detected_exact<bool, number_integer_function_t, SAX,\n        number_integer_t>::value,\n        \"Missing/invalid function: bool number_integer(number_integer_t)\");\n    static_assert(\n        is_detected_exact<bool, number_unsigned_function_t, SAX,\n        number_unsigned_t>::value,\n        \"Missing/invalid function: bool number_unsigned(number_unsigned_t)\");\n    static_assert(is_detected_exact<bool, number_float_function_t, SAX,\n                  number_float_t, string_t>::value,\n                  \"Missing/invalid function: bool number_float(number_float_t, const string_t&)\");\n    static_assert(\n        is_detected_exact<bool, string_function_t, SAX, string_t>::value,\n        \"Missing/invalid function: bool string(string_t&)\");\n    static_assert(is_detected_exact<bool, start_object_function_t, SAX>::value,\n                  \"Missing/invalid function: bool start_object(std::size_t)\");\n    static_assert(is_detected_exact<bool, key_function_t, SAX, string_t>::value,\n                  \"Missing/invalid function: bool key(string_t&)\");\n    static_assert(is_detected_exact<bool, end_object_function_t, SAX>::value,\n                  \"Missing/invalid function: bool end_object()\");\n    static_assert(is_detected_exact<bool, start_array_function_t, SAX>::value,\n                  \"Missing/invalid function: bool start_array(std::size_t)\");\n    static_assert(is_detected_exact<bool, end_array_function_t, SAX>::value,\n                  \"Missing/invalid function: bool end_array()\");\n    static_assert(\n        is_detected_exact<bool, parse_error_function_t, SAX, exception_t>::value,\n        \"Missing/invalid function: bool parse_error(std::size_t, const \"\n        \"std::string&, const exception&)\");\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/json_sax.hpp>\n\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\n// #include <nlohmann/detail/input/parser.hpp>\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n\nnamespace nlohmann\n{\n\n/*!\n@brief SAX interface\n\nThis class describes the SAX interface used by @ref nlohmann::json::sax_parse.\nEach function is called in different situations while the input is parsed. The\nboolean return value informs the parser whether to continue processing the\ninput.\n*/\ntemplate<typename BasicJsonType>\nstruct json_sax\n{\n    /// type for (signed) integers\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    /// type for unsigned integers\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    /// type for floating-point numbers\n    using number_float_t = typename BasicJsonType::number_float_t;\n    /// type for strings\n    using string_t = typename BasicJsonType::string_t;\n\n    /*!\n    @brief a null value was read\n    @return whether parsing should proceed\n    */\n    virtual bool null() = 0;\n\n    /*!\n    @brief a boolean value was read\n    @param[in] val  boolean value\n    @return whether parsing should proceed\n    */\n    virtual bool boolean(bool val) = 0;\n\n    /*!\n    @brief an integer number was read\n    @param[in] val  integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_integer(number_integer_t val) = 0;\n\n    /*!\n    @brief an unsigned integer number was read\n    @param[in] val  unsigned integer value\n    @return whether parsing should proceed\n    */\n    virtual bool number_unsigned(number_unsigned_t val) = 0;\n\n    /*!\n    @brief an floating-point number was read\n    @param[in] val  floating-point value\n    @param[in] s    raw token value\n    @return whether parsing should proceed\n    */\n    virtual bool number_float(number_float_t val, const string_t& s) = 0;\n\n    /*!\n    @brief a string was read\n    @param[in] val  string value\n    @return whether parsing should proceed\n    @note It is safe to move the passed string.\n    */\n    virtual bool string(string_t& val) = 0;\n\n    /*!\n    @brief the beginning of an object was read\n    @param[in] elements  number of object elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_object(std::size_t elements) = 0;\n\n    /*!\n    @brief an object key was read\n    @param[in] val  object key\n    @return whether parsing should proceed\n    @note It is safe to move the passed string.\n    */\n    virtual bool key(string_t& val) = 0;\n\n    /*!\n    @brief the end of an object was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_object() = 0;\n\n    /*!\n    @brief the beginning of an array was read\n    @param[in] elements  number of array elements or -1 if unknown\n    @return whether parsing should proceed\n    @note binary formats may report the number of elements\n    */\n    virtual bool start_array(std::size_t elements) = 0;\n\n    /*!\n    @brief the end of an array was read\n    @return whether parsing should proceed\n    */\n    virtual bool end_array() = 0;\n\n    /*!\n    @brief a parse error occurred\n    @param[in] position    the position in the input where the error occurs\n    @param[in] last_token  the last read token\n    @param[in] ex          an exception object describing the error\n    @return whether parsing should proceed (must return false)\n    */\n    virtual bool parse_error(std::size_t position,\n                             const std::string& last_token,\n                             const detail::exception& ex) = 0;\n\n    virtual ~json_sax() = default;\n};\n\n\nnamespace detail\n{\n/*!\n@brief SAX implementation to create a JSON value from SAX events\n\nThis class implements the @ref json_sax interface and processes the SAX events\nto create a JSON value which makes it basically a DOM parser. The structure or\nhierarchy of the JSON value is managed by the stack `ref_stack` which contains\na pointer to the respective array or object for each recursion depth.\n\nAfter successful parsing, the value that is passed by reference to the\nconstructor contains the parsed value.\n\n@tparam BasicJsonType  the JSON type\n*/\ntemplate<typename BasicJsonType>\nclass json_sax_dom_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n\n    /*!\n    @param[in, out] r  reference to a JSON value that is manipulated while\n                       parsing\n    @param[in] allow_exceptions_  whether parse errors yield exceptions\n    */\n    explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true)\n        : root(r), allow_exceptions(allow_exceptions_)\n    {}\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::object));\n\n        if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408,\n                                            \"excessive object size: \" + std::to_string(len)));\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        // add null at given key and store the reference for later\n        object_element = &(ref_stack.back()->m_value.object->operator[](val));\n        return true;\n    }\n\n    bool end_object()\n    {\n        ref_stack.pop_back();\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        ref_stack.push_back(handle_value(BasicJsonType::value_t::array));\n\n        if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))\n        {\n            JSON_THROW(out_of_range::create(408,\n                                            \"excessive array size: \" + std::to_string(len)));\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        ref_stack.pop_back();\n        return true;\n    }\n\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const detail::exception& ex)\n    {\n        errored = true;\n        if (allow_exceptions)\n        {\n            // determine the proper exception type from the id\n            switch ((ex.id / 100) % 100)\n            {\n                case 1:\n                    JSON_THROW(*reinterpret_cast<const detail::parse_error*>(&ex));\n                case 4:\n                    JSON_THROW(*reinterpret_cast<const detail::out_of_range*>(&ex));\n                // LCOV_EXCL_START\n                case 2:\n                    JSON_THROW(*reinterpret_cast<const detail::invalid_iterator*>(&ex));\n                case 3:\n                    JSON_THROW(*reinterpret_cast<const detail::type_error*>(&ex));\n                case 5:\n                    JSON_THROW(*reinterpret_cast<const detail::other_error*>(&ex));\n                default:\n                    assert(false);\n                    // LCOV_EXCL_STOP\n            }\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n    /*!\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n    */\n    template<typename Value>\n    BasicJsonType* handle_value(Value&& v)\n    {\n        if (ref_stack.empty())\n        {\n            root = BasicJsonType(std::forward<Value>(v));\n            return &root;\n        }\n\n        assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());\n\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->emplace_back(std::forward<Value>(v));\n            return &(ref_stack.back()->m_value.array->back());\n        }\n        else\n        {\n            assert(object_element);\n            *object_element = BasicJsonType(std::forward<Value>(v));\n            return object_element;\n        }\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack;\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_dom_callback_parser\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using parser_callback_t = typename BasicJsonType::parser_callback_t;\n    using parse_event_t = typename BasicJsonType::parse_event_t;\n\n    json_sax_dom_callback_parser(BasicJsonType& r,\n                                 const parser_callback_t cb,\n                                 const bool allow_exceptions_ = true)\n        : root(r), callback(cb), allow_exceptions(allow_exceptions_)\n    {\n        keep_stack.push_back(true);\n    }\n\n    bool null()\n    {\n        handle_value(nullptr);\n        return true;\n    }\n\n    bool boolean(bool val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_integer(number_integer_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool number_float(number_float_t val, const string_t& /*unused*/)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool string(string_t& val)\n    {\n        handle_value(val);\n        return true;\n    }\n\n    bool start_object(std::size_t len)\n    {\n        // check callback for object start\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::object_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::object, true);\n        ref_stack.push_back(val.second);\n\n        // check object limit\n        if (ref_stack.back())\n        {\n            if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))\n            {\n                JSON_THROW(out_of_range::create(408,\n                                                \"excessive object size: \" + std::to_string(len)));\n            }\n        }\n\n        return true;\n    }\n\n    bool key(string_t& val)\n    {\n        BasicJsonType k = BasicJsonType(val);\n\n        // check callback for key\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::key, k);\n        key_keep_stack.push_back(keep);\n\n        // add discarded value at given key and store the reference for later\n        if (keep and ref_stack.back())\n        {\n            object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded);\n        }\n\n        return true;\n    }\n\n    bool end_object()\n    {\n        if (ref_stack.back())\n        {\n            if (not callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))\n            {\n                // discard object\n                *ref_stack.back() = discarded;\n            }\n        }\n\n        assert(not ref_stack.empty());\n        assert(not keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        if (not ref_stack.empty() and ref_stack.back())\n        {\n            // remove discarded value\n            if (ref_stack.back()->is_object())\n            {\n                for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it)\n                {\n                    if (it->is_discarded())\n                    {\n                        ref_stack.back()->erase(it);\n                        break;\n                    }\n                }\n            }\n        }\n\n        return true;\n    }\n\n    bool start_array(std::size_t len)\n    {\n        const bool keep = callback(static_cast<int>(ref_stack.size()), parse_event_t::array_start, discarded);\n        keep_stack.push_back(keep);\n\n        auto val = handle_value(BasicJsonType::value_t::array, true);\n        ref_stack.push_back(val.second);\n\n        // check array limit\n        if (ref_stack.back())\n        {\n            if (JSON_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size()))\n            {\n                JSON_THROW(out_of_range::create(408,\n                                                \"excessive array size: \" + std::to_string(len)));\n            }\n        }\n\n        return true;\n    }\n\n    bool end_array()\n    {\n        bool keep = true;\n\n        if (ref_stack.back())\n        {\n            keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());\n            if (not keep)\n            {\n                // discard array\n                *ref_stack.back() = discarded;\n            }\n        }\n\n        assert(not ref_stack.empty());\n        assert(not keep_stack.empty());\n        ref_stack.pop_back();\n        keep_stack.pop_back();\n\n        // remove discarded value\n        if (not keep and not ref_stack.empty())\n        {\n            if (ref_stack.back()->is_array())\n            {\n                ref_stack.back()->m_value.array->pop_back();\n            }\n        }\n\n        return true;\n    }\n\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/,\n                     const detail::exception& ex)\n    {\n        errored = true;\n        if (allow_exceptions)\n        {\n            // determine the proper exception type from the id\n            switch ((ex.id / 100) % 100)\n            {\n                case 1:\n                    JSON_THROW(*reinterpret_cast<const detail::parse_error*>(&ex));\n                case 4:\n                    JSON_THROW(*reinterpret_cast<const detail::out_of_range*>(&ex));\n                // LCOV_EXCL_START\n                case 2:\n                    JSON_THROW(*reinterpret_cast<const detail::invalid_iterator*>(&ex));\n                case 3:\n                    JSON_THROW(*reinterpret_cast<const detail::type_error*>(&ex));\n                case 5:\n                    JSON_THROW(*reinterpret_cast<const detail::other_error*>(&ex));\n                default:\n                    assert(false);\n                    // LCOV_EXCL_STOP\n            }\n        }\n        return false;\n    }\n\n    constexpr bool is_errored() const\n    {\n        return errored;\n    }\n\n  private:\n    /*!\n    @param[in] v  value to add to the JSON value we build during parsing\n    @param[in] skip_callback  whether we should skip calling the callback\n               function; this is required after start_array() and\n               start_object() SAX events, because otherwise we would call the\n               callback function with an empty array or object, respectively.\n\n    @invariant If the ref stack is empty, then the passed value will be the new\n               root.\n    @invariant If the ref stack contains a value, then it is an array or an\n               object to which we can add elements\n\n    @return pair of boolean (whether value should be kept) and pointer (to the\n            passed value in the ref_stack hierarchy; nullptr if not kept)\n    */\n    template<typename Value>\n    std::pair<bool, BasicJsonType*> handle_value(Value&& v, const bool skip_callback = false)\n    {\n        assert(not keep_stack.empty());\n\n        // do not handle this value if we know it would be added to a discarded\n        // container\n        if (not keep_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // create value\n        auto value = BasicJsonType(std::forward<Value>(v));\n\n        // check callback\n        const bool keep = skip_callback or callback(static_cast<int>(ref_stack.size()), parse_event_t::value, value);\n\n        // do not handle this value if we just learnt it shall be discarded\n        if (not keep)\n        {\n            return {false, nullptr};\n        }\n\n        if (ref_stack.empty())\n        {\n            root = std::move(value);\n            return {true, &root};\n        }\n\n        // skip this value if we already decided to skip the parent\n        // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360)\n        if (not ref_stack.back())\n        {\n            return {false, nullptr};\n        }\n\n        // we now only expect arrays and objects\n        assert(ref_stack.back()->is_array() or ref_stack.back()->is_object());\n\n        if (ref_stack.back()->is_array())\n        {\n            ref_stack.back()->m_value.array->push_back(std::move(value));\n            return {true, &(ref_stack.back()->m_value.array->back())};\n        }\n        else\n        {\n            // check if we should store an element for the current key\n            assert(not key_keep_stack.empty());\n            const bool store_element = key_keep_stack.back();\n            key_keep_stack.pop_back();\n\n            if (not store_element)\n            {\n                return {false, nullptr};\n            }\n\n            assert(object_element);\n            *object_element = std::move(value);\n            return {true, object_element};\n        }\n    }\n\n    /// the parsed JSON value\n    BasicJsonType& root;\n    /// stack to model hierarchy of values\n    std::vector<BasicJsonType*> ref_stack;\n    /// stack to manage which values to keep\n    std::vector<bool> keep_stack;\n    /// stack to manage which object keys to keep\n    std::vector<bool> key_keep_stack;\n    /// helper to hold the reference for the next object element\n    BasicJsonType* object_element = nullptr;\n    /// whether a syntax error occurred\n    bool errored = false;\n    /// callback function\n    const parser_callback_t callback = nullptr;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n    /// a discarded value for the callback\n    BasicJsonType discarded = BasicJsonType::value_t::discarded;\n};\n\ntemplate<typename BasicJsonType>\nclass json_sax_acceptor\n{\n  public:\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n\n    bool null()\n    {\n        return true;\n    }\n\n    bool boolean(bool /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_integer(number_integer_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_unsigned(number_unsigned_t /*unused*/)\n    {\n        return true;\n    }\n\n    bool number_float(number_float_t /*unused*/, const string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool string(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool start_object(std::size_t  /*unused*/ = std::size_t(-1))\n    {\n        return true;\n    }\n\n    bool key(string_t& /*unused*/)\n    {\n        return true;\n    }\n\n    bool end_object()\n    {\n        return true;\n    }\n\n    bool start_array(std::size_t  /*unused*/ = std::size_t(-1))\n    {\n        return true;\n    }\n\n    bool end_array()\n    {\n        return true;\n    }\n\n    bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/)\n    {\n        return false;\n    }\n};\n}  // namespace detail\n\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/lexer.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n////////////\n// parser //\n////////////\n\n/*!\n@brief syntax analysis\n\nThis class implements a recursive decent parser.\n*/\ntemplate<typename BasicJsonType>\nclass parser\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using lexer_t = lexer<BasicJsonType>;\n    using token_type = typename lexer_t::token_type;\n\n  public:\n    enum class parse_event_t : uint8_t\n    {\n        /// the parser read `{` and started to process a JSON object\n        object_start,\n        /// the parser read `}` and finished processing a JSON object\n        object_end,\n        /// the parser read `[` and started to process a JSON array\n        array_start,\n        /// the parser read `]` and finished processing a JSON array\n        array_end,\n        /// the parser read a key of a value in an object\n        key,\n        /// the parser finished reading a JSON value\n        value\n    };\n\n    using parser_callback_t =\n        std::function<bool(int depth, parse_event_t event, BasicJsonType& parsed)>;\n\n    /// a parser reading from an input adapter\n    explicit parser(detail::input_adapter_t&& adapter,\n                    const parser_callback_t cb = nullptr,\n                    const bool allow_exceptions_ = true)\n        : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_)\n    {\n        // read first token\n        get_token();\n    }\n\n    /*!\n    @brief public parser interface\n\n    @param[in] strict      whether to expect the last token to be EOF\n    @param[in,out] result  parsed JSON value\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n    */\n    void parse(const bool strict, BasicJsonType& result)\n    {\n        if (callback)\n        {\n            json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);\n            sax_parse_internal(&sdp);\n            result.assert_invariant();\n\n            // in strict mode, input must be completely read\n            if (strict and (get_token() != token_type::end_of_input))\n            {\n                sdp.parse_error(m_lexer.get_position(),\n                                m_lexer.get_token_string(),\n                                parse_error::create(101, m_lexer.get_position(),\n                                                    exception_message(token_type::end_of_input, \"value\")));\n            }\n\n            // in case of an error, return discarded value\n            if (sdp.is_errored())\n            {\n                result = value_t::discarded;\n                return;\n            }\n\n            // set top-level value to null if it was discarded by the callback\n            // function\n            if (result.is_discarded())\n            {\n                result = nullptr;\n            }\n        }\n        else\n        {\n            json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);\n            sax_parse_internal(&sdp);\n            result.assert_invariant();\n\n            // in strict mode, input must be completely read\n            if (strict and (get_token() != token_type::end_of_input))\n            {\n                sdp.parse_error(m_lexer.get_position(),\n                                m_lexer.get_token_string(),\n                                parse_error::create(101, m_lexer.get_position(),\n                                                    exception_message(token_type::end_of_input, \"value\")));\n            }\n\n            // in case of an error, return discarded value\n            if (sdp.is_errored())\n            {\n                result = value_t::discarded;\n                return;\n            }\n        }\n    }\n\n    /*!\n    @brief public accept interface\n\n    @param[in] strict  whether to expect the last token to be EOF\n    @return whether the input is a proper JSON text\n    */\n    bool accept(const bool strict = true)\n    {\n        json_sax_acceptor<BasicJsonType> sax_acceptor;\n        return sax_parse(&sax_acceptor, strict);\n    }\n\n    template <typename SAX>\n    bool sax_parse(SAX* sax, const bool strict = true)\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n        const bool result = sax_parse_internal(sax);\n\n        // strict mode: next byte must be EOF\n        if (result and strict and (get_token() != token_type::end_of_input))\n        {\n            return sax->parse_error(m_lexer.get_position(),\n                                    m_lexer.get_token_string(),\n                                    parse_error::create(101, m_lexer.get_position(),\n                                            exception_message(token_type::end_of_input, \"value\")));\n        }\n\n        return result;\n    }\n\n  private:\n    template <typename SAX>\n    bool sax_parse_internal(SAX* sax)\n    {\n        // stack to remember the hierarchy of structured values we are parsing\n        // true = array; false = object\n        std::vector<bool> states;\n        // value to avoid a goto (see comment where set to true)\n        bool skip_to_state_evaluation = false;\n\n        while (true)\n        {\n            if (not skip_to_state_evaluation)\n            {\n                // invariant: get_token() was called before each iteration\n                switch (last_token)\n                {\n                    case token_type::begin_object:\n                    {\n                        if (JSON_UNLIKELY(not sax->start_object(std::size_t(-1))))\n                        {\n                            return false;\n                        }\n\n                        // closing } -> we are done\n                        if (get_token() == token_type::end_object)\n                        {\n                            if (JSON_UNLIKELY(not sax->end_object()))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n\n                        // parse key\n                        if (JSON_UNLIKELY(last_token != token_type::value_string))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(),\n                                                            exception_message(token_type::value_string, \"object key\")));\n                        }\n                        if (JSON_UNLIKELY(not sax->key(m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n\n                        // parse separator (:)\n                        if (JSON_UNLIKELY(get_token() != token_type::name_separator))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(),\n                                                            exception_message(token_type::name_separator, \"object separator\")));\n                        }\n\n                        // remember we are now inside an object\n                        states.push_back(false);\n\n                        // parse values\n                        get_token();\n                        continue;\n                    }\n\n                    case token_type::begin_array:\n                    {\n                        if (JSON_UNLIKELY(not sax->start_array(std::size_t(-1))))\n                        {\n                            return false;\n                        }\n\n                        // closing ] -> we are done\n                        if (get_token() == token_type::end_array)\n                        {\n                            if (JSON_UNLIKELY(not sax->end_array()))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n\n                        // remember we are now inside an array\n                        states.push_back(true);\n\n                        // parse values (no need to call get_token)\n                        continue;\n                    }\n\n                    case token_type::value_float:\n                    {\n                        const auto res = m_lexer.get_number_float();\n\n                        if (JSON_UNLIKELY(not std::isfinite(res)))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    out_of_range::create(406, \"number overflow parsing '\" + m_lexer.get_token_string() + \"'\"));\n                        }\n                        else\n                        {\n                            if (JSON_UNLIKELY(not sax->number_float(res, m_lexer.get_string())))\n                            {\n                                return false;\n                            }\n                            break;\n                        }\n                    }\n\n                    case token_type::literal_false:\n                    {\n                        if (JSON_UNLIKELY(not sax->boolean(false)))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::literal_null:\n                    {\n                        if (JSON_UNLIKELY(not sax->null()))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::literal_true:\n                    {\n                        if (JSON_UNLIKELY(not sax->boolean(true)))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_integer:\n                    {\n                        if (JSON_UNLIKELY(not sax->number_integer(m_lexer.get_number_integer())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_string:\n                    {\n                        if (JSON_UNLIKELY(not sax->string(m_lexer.get_string())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::value_unsigned:\n                    {\n                        if (JSON_UNLIKELY(not sax->number_unsigned(m_lexer.get_number_unsigned())))\n                        {\n                            return false;\n                        }\n                        break;\n                    }\n\n                    case token_type::parse_error:\n                    {\n                        // using \"uninitialized\" to avoid \"expected\" message\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::uninitialized, \"value\")));\n                    }\n\n                    default: // the last token was unexpected\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::literal_or_value, \"value\")));\n                    }\n                }\n            }\n            else\n            {\n                skip_to_state_evaluation = false;\n            }\n\n            // we reached this line after we successfully parsed a value\n            if (states.empty())\n            {\n                // empty stack: we reached the end of the hierarchy: done\n                return true;\n            }\n            else\n            {\n                if (states.back())  // array\n                {\n                    // comma -> next value\n                    if (get_token() == token_type::value_separator)\n                    {\n                        // parse a new value\n                        get_token();\n                        continue;\n                    }\n\n                    // closing ]\n                    if (JSON_LIKELY(last_token == token_type::end_array))\n                    {\n                        if (JSON_UNLIKELY(not sax->end_array()))\n                        {\n                            return false;\n                        }\n\n                        // We are done with this array. Before we can parse a\n                        // new value, we need to evaluate the new state first.\n                        // By setting skip_to_state_evaluation to false, we\n                        // are effectively jumping to the beginning of this if.\n                        assert(not states.empty());\n                        states.pop_back();\n                        skip_to_state_evaluation = true;\n                        continue;\n                    }\n                    else\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::end_array, \"array\")));\n                    }\n                }\n                else  // object\n                {\n                    // comma -> next value\n                    if (get_token() == token_type::value_separator)\n                    {\n                        // parse key\n                        if (JSON_UNLIKELY(get_token() != token_type::value_string))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(),\n                                                            exception_message(token_type::value_string, \"object key\")));\n                        }\n                        else\n                        {\n                            if (JSON_UNLIKELY(not sax->key(m_lexer.get_string())))\n                            {\n                                return false;\n                            }\n                        }\n\n                        // parse separator (:)\n                        if (JSON_UNLIKELY(get_token() != token_type::name_separator))\n                        {\n                            return sax->parse_error(m_lexer.get_position(),\n                                                    m_lexer.get_token_string(),\n                                                    parse_error::create(101, m_lexer.get_position(),\n                                                            exception_message(token_type::name_separator, \"object separator\")));\n                        }\n\n                        // parse values\n                        get_token();\n                        continue;\n                    }\n\n                    // closing }\n                    if (JSON_LIKELY(last_token == token_type::end_object))\n                    {\n                        if (JSON_UNLIKELY(not sax->end_object()))\n                        {\n                            return false;\n                        }\n\n                        // We are done with this object. Before we can parse a\n                        // new value, we need to evaluate the new state first.\n                        // By setting skip_to_state_evaluation to false, we\n                        // are effectively jumping to the beginning of this if.\n                        assert(not states.empty());\n                        states.pop_back();\n                        skip_to_state_evaluation = true;\n                        continue;\n                    }\n                    else\n                    {\n                        return sax->parse_error(m_lexer.get_position(),\n                                                m_lexer.get_token_string(),\n                                                parse_error::create(101, m_lexer.get_position(),\n                                                        exception_message(token_type::end_object, \"object\")));\n                    }\n                }\n            }\n        }\n    }\n\n    /// get next token from lexer\n    token_type get_token()\n    {\n        return (last_token = m_lexer.scan());\n    }\n\n    std::string exception_message(const token_type expected, const std::string& context)\n    {\n        std::string error_msg = \"syntax error \";\n\n        if (not context.empty())\n        {\n            error_msg += \"while parsing \" + context + \" \";\n        }\n\n        error_msg += \"- \";\n\n        if (last_token == token_type::parse_error)\n        {\n            error_msg += std::string(m_lexer.get_error_message()) + \"; last read: '\" +\n                         m_lexer.get_token_string() + \"'\";\n        }\n        else\n        {\n            error_msg += \"unexpected \" + std::string(lexer_t::token_type_name(last_token));\n        }\n\n        if (expected != token_type::uninitialized)\n        {\n            error_msg += \"; expected \" + std::string(lexer_t::token_type_name(expected));\n        }\n\n        return error_msg;\n    }\n\n  private:\n    /// callback function\n    const parser_callback_t callback = nullptr;\n    /// the type of the last read token\n    token_type last_token = token_type::uninitialized;\n    /// the lexer\n    lexer_t m_lexer;\n    /// whether to throw exceptions in case of errors\n    const bool allow_exceptions = true;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n\n#include <cstddef> // ptrdiff_t\n#include <limits>  // numeric_limits\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/*\n@brief an iterator for primitive JSON types\n\nThis class models an iterator for primitive JSON types (boolean, number,\nstring). It's only purpose is to allow the iterator/const_iterator classes\nto \"iterate\" over primitive values. Internally, the iterator is modeled by\na `difference_type` variable. Value begin_value (`0`) models the begin,\nend_value (`1`) models past the end.\n*/\nclass primitive_iterator_t\n{\n  private:\n    using difference_type = std::ptrdiff_t;\n    static constexpr difference_type begin_value = 0;\n    static constexpr difference_type end_value = begin_value + 1;\n\n    /// iterator as signed integer type\n    difference_type m_it = (std::numeric_limits<std::ptrdiff_t>::min)();\n\n  public:\n    constexpr difference_type get_value() const noexcept\n    {\n        return m_it;\n    }\n\n    /// set iterator to a defined beginning\n    void set_begin() noexcept\n    {\n        m_it = begin_value;\n    }\n\n    /// set iterator to a defined past the end\n    void set_end() noexcept\n    {\n        m_it = end_value;\n    }\n\n    /// return whether the iterator can be dereferenced\n    constexpr bool is_begin() const noexcept\n    {\n        return m_it == begin_value;\n    }\n\n    /// return whether the iterator is at end\n    constexpr bool is_end() const noexcept\n    {\n        return m_it == end_value;\n    }\n\n    friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it == rhs.m_it;\n    }\n\n    friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it < rhs.m_it;\n    }\n\n    primitive_iterator_t operator+(difference_type n) noexcept\n    {\n        auto result = *this;\n        result += n;\n        return result;\n    }\n\n    friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept\n    {\n        return lhs.m_it - rhs.m_it;\n    }\n\n    primitive_iterator_t& operator++() noexcept\n    {\n        ++m_it;\n        return *this;\n    }\n\n    primitive_iterator_t const operator++(int) noexcept\n    {\n        auto result = *this;\n        ++m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator--() noexcept\n    {\n        --m_it;\n        return *this;\n    }\n\n    primitive_iterator_t const operator--(int) noexcept\n    {\n        auto result = *this;\n        --m_it;\n        return result;\n    }\n\n    primitive_iterator_t& operator+=(difference_type n) noexcept\n    {\n        m_it += n;\n        return *this;\n    }\n\n    primitive_iterator_t& operator-=(difference_type n) noexcept\n    {\n        m_it -= n;\n        return *this;\n    }\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/internal_iterator.hpp>\n\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/*!\n@brief an iterator value\n\n@note This structure could easily be a union, but MSVC currently does not allow\nunions members with complex constructors, see https://github.com/nlohmann/json/pull/105.\n*/\ntemplate<typename BasicJsonType> struct internal_iterator\n{\n    /// iterator for JSON objects\n    typename BasicJsonType::object_t::iterator object_iterator {};\n    /// iterator for JSON arrays\n    typename BasicJsonType::array_t::iterator array_iterator {};\n    /// generic iterator for all other types\n    primitive_iterator_t primitive_iterator {};\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/iter_impl.hpp>\n\n\n#include <ciso646> // not\n#include <iterator> // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next\n#include <type_traits> // conditional, is_const, remove_const\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/iterators/internal_iterator.hpp>\n\n// #include <nlohmann/detail/iterators/primitive_iterator.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n// forward declare, to be able to friend it later on\ntemplate<typename IteratorType> class iteration_proxy;\n\n/*!\n@brief a template for a bidirectional iterator for the @ref basic_json class\n\nThis class implements a both iterators (iterator and const_iterator) for the\n@ref basic_json class.\n\n@note An iterator is called *initialized* when a pointer to a JSON value has\n      been set (e.g., by a constructor or a copy assignment). If the iterator is\n      default-constructed, it is *uninitialized* and most methods are undefined.\n      **The library uses assertions to detect calls on uninitialized iterators.**\n\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n\n@since version 1.0.0, simplified in version 2.0.9, change to bidirectional\n       iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593)\n*/\ntemplate<typename BasicJsonType>\nclass iter_impl\n{\n    /// allow basic_json to access private members\n    friend iter_impl<typename std::conditional<std::is_const<BasicJsonType>::value, typename std::remove_const<BasicJsonType>::type, const BasicJsonType>::type>;\n    friend BasicJsonType;\n    friend iteration_proxy<iter_impl>;\n\n    using object_t = typename BasicJsonType::object_t;\n    using array_t = typename BasicJsonType::array_t;\n    // make sure BasicJsonType is basic_json or const basic_json\n    static_assert(is_basic_json<typename std::remove_const<BasicJsonType>::type>::value,\n                  \"iter_impl only accepts (const) basic_json\");\n\n  public:\n\n    /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17.\n    /// The C++ Standard has never required user-defined iterators to derive from std::iterator.\n    /// A user-defined iterator should provide publicly accessible typedefs named\n    /// iterator_category, value_type, difference_type, pointer, and reference.\n    /// Note that value_type is required to be non-const, even for constant iterators.\n    using iterator_category = std::bidirectional_iterator_tag;\n\n    /// the type of the values when the iterator is dereferenced\n    using value_type = typename BasicJsonType::value_type;\n    /// a type to represent differences between iterators\n    using difference_type = typename BasicJsonType::difference_type;\n    /// defines a pointer to the type iterated over (value_type)\n    using pointer = typename std::conditional<std::is_const<BasicJsonType>::value,\n          typename BasicJsonType::const_pointer,\n          typename BasicJsonType::pointer>::type;\n    /// defines a reference to the type iterated over (value_type)\n    using reference =\n        typename std::conditional<std::is_const<BasicJsonType>::value,\n        typename BasicJsonType::const_reference,\n        typename BasicJsonType::reference>::type;\n\n    /// default constructor\n    iter_impl() = default;\n\n    /*!\n    @brief constructor for a given JSON instance\n    @param[in] object  pointer to a JSON object for this iterator\n    @pre object != nullptr\n    @post The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    explicit iter_impl(pointer object) noexcept : m_object(object)\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = typename object_t::iterator();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = typename array_t::iterator();\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator = primitive_iterator_t();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @note The conventional copy constructor and copy assignment are implicitly\n          defined. Combined with the following converting constructor and\n          assignment, they support: (1) copy from iterator to iterator, (2)\n          copy from const iterator to const iterator, and (3) conversion from\n          iterator to const iterator. However conversion from const iterator\n          to iterator is not defined.\n    */\n\n    /*!\n    @brief converting constructor\n    @param[in] other  non-const iterator to copy from\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept\n        : m_object(other.m_object), m_it(other.m_it) {}\n\n    /*!\n    @brief converting assignment\n    @param[in,out] other  non-const iterator to copy from\n    @return const/non-const iterator\n    @note It is not checked whether @a other is initialized.\n    */\n    iter_impl& operator=(const iter_impl<typename std::remove_const<BasicJsonType>::type>& other) noexcept\n    {\n        m_object = other.m_object;\n        m_it = other.m_it;\n        return *this;\n    }\n\n  private:\n    /*!\n    @brief set the iterator to the first value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_begin() noexcept\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_value.object->begin();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_value.array->begin();\n                break;\n            }\n\n            case value_t::null:\n            {\n                // set to end so begin()==end() is true: null is empty\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator.set_begin();\n                break;\n            }\n        }\n    }\n\n    /*!\n    @brief set the iterator past the last value\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    void set_end() noexcept\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                m_it.object_iterator = m_object->m_value.object->end();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_it.array_iterator = m_object->m_value.array->end();\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator.set_end();\n                break;\n            }\n        }\n    }\n\n  public:\n    /*!\n    @brief return a reference to the value pointed to by the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator*() const\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                assert(m_it.object_iterator != m_object->m_value.object->end());\n                return m_it.object_iterator->second;\n            }\n\n            case value_t::array:\n            {\n                assert(m_it.array_iterator != m_object->m_value.array->end());\n                return *m_it.array_iterator;\n            }\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n\n            default:\n            {\n                if (JSON_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n            }\n        }\n    }\n\n    /*!\n    @brief dereference the iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    pointer operator->() const\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                assert(m_it.object_iterator != m_object->m_value.object->end());\n                return &(m_it.object_iterator->second);\n            }\n\n            case value_t::array:\n            {\n                assert(m_it.array_iterator != m_object->m_value.array->end());\n                return &*m_it.array_iterator;\n            }\n\n            default:\n            {\n                if (JSON_LIKELY(m_it.primitive_iterator.is_begin()))\n                {\n                    return m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n            }\n        }\n    }\n\n    /*!\n    @brief post-increment (it++)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl const operator++(int)\n    {\n        auto result = *this;\n        ++(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-increment (++it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator++()\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, 1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, 1);\n                break;\n            }\n\n            default:\n            {\n                ++m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief post-decrement (it--)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl const operator--(int)\n    {\n        auto result = *this;\n        --(*this);\n        return result;\n    }\n\n    /*!\n    @brief pre-decrement (--it)\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator--()\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n            {\n                std::advance(m_it.object_iterator, -1);\n                break;\n            }\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, -1);\n                break;\n            }\n\n            default:\n            {\n                --m_it.primitive_iterator;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief  comparison: equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator==(const iter_impl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\"));\n        }\n\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                return (m_it.object_iterator == other.m_it.object_iterator);\n\n            case value_t::array:\n                return (m_it.array_iterator == other.m_it.array_iterator);\n\n            default:\n                return (m_it.primitive_iterator == other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief  comparison: not equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator!=(const iter_impl& other) const\n    {\n        return not operator==(other);\n    }\n\n    /*!\n    @brief  comparison: smaller\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator<(const iter_impl& other) const\n    {\n        // if objects are not the same, the comparison is undefined\n        if (JSON_UNLIKELY(m_object != other.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(212, \"cannot compare iterators of different containers\"));\n        }\n\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(213, \"cannot compare order of object iterators\"));\n\n            case value_t::array:\n                return (m_it.array_iterator < other.m_it.array_iterator);\n\n            default:\n                return (m_it.primitive_iterator < other.m_it.primitive_iterator);\n        }\n    }\n\n    /*!\n    @brief  comparison: less than or equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator<=(const iter_impl& other) const\n    {\n        return not other.operator < (*this);\n    }\n\n    /*!\n    @brief  comparison: greater than\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator>(const iter_impl& other) const\n    {\n        return not operator<=(other);\n    }\n\n    /*!\n    @brief  comparison: greater than or equal\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    bool operator>=(const iter_impl& other) const\n    {\n        return not operator<(other);\n    }\n\n    /*!\n    @brief  add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator+=(difference_type i)\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\"));\n\n            case value_t::array:\n            {\n                std::advance(m_it.array_iterator, i);\n                break;\n            }\n\n            default:\n            {\n                m_it.primitive_iterator += i;\n                break;\n            }\n        }\n\n        return *this;\n    }\n\n    /*!\n    @brief  subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl& operator-=(difference_type i)\n    {\n        return operator+=(-i);\n    }\n\n    /*!\n    @brief  add to iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator+(difference_type i) const\n    {\n        auto result = *this;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief  addition of distance and iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    friend iter_impl operator+(difference_type i, const iter_impl& it)\n    {\n        auto result = it;\n        result += i;\n        return result;\n    }\n\n    /*!\n    @brief  subtract from iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    iter_impl operator-(difference_type i) const\n    {\n        auto result = *this;\n        result -= i;\n        return result;\n    }\n\n    /*!\n    @brief  return difference\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    difference_type operator-(const iter_impl& other) const\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(209, \"cannot use offsets with object iterators\"));\n\n            case value_t::array:\n                return m_it.array_iterator - other.m_it.array_iterator;\n\n            default:\n                return m_it.primitive_iterator - other.m_it.primitive_iterator;\n        }\n    }\n\n    /*!\n    @brief  access to successor\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference operator[](difference_type n) const\n    {\n        assert(m_object != nullptr);\n\n        switch (m_object->m_type)\n        {\n            case value_t::object:\n                JSON_THROW(invalid_iterator::create(208, \"cannot use operator[] for object iterators\"));\n\n            case value_t::array:\n                return *std::next(m_it.array_iterator, n);\n\n            case value_t::null:\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n\n            default:\n            {\n                if (JSON_LIKELY(m_it.primitive_iterator.get_value() == -n))\n                {\n                    return *m_object;\n                }\n\n                JSON_THROW(invalid_iterator::create(214, \"cannot get value\"));\n            }\n        }\n    }\n\n    /*!\n    @brief  return the key of an object iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    const typename object_t::key_type& key() const\n    {\n        assert(m_object != nullptr);\n\n        if (JSON_LIKELY(m_object->is_object()))\n        {\n            return m_it.object_iterator->first;\n        }\n\n        JSON_THROW(invalid_iterator::create(207, \"cannot use key() for non-object iterators\"));\n    }\n\n    /*!\n    @brief  return the value of an iterator\n    @pre The iterator is initialized; i.e. `m_object != nullptr`.\n    */\n    reference value() const\n    {\n        return operator*();\n    }\n\n  private:\n    /// associated JSON instance\n    pointer m_object = nullptr;\n    /// the actual iterator of the associated instance\n    internal_iterator<typename std::remove_const<BasicJsonType>::type> m_it;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/iterators/iteration_proxy.hpp>\n\n// #include <nlohmann/detail/iterators/json_reverse_iterator.hpp>\n\n\n#include <cstddef> // ptrdiff_t\n#include <iterator> // reverse_iterator\n#include <utility> // declval\n\nnamespace nlohmann\n{\nnamespace detail\n{\n//////////////////////\n// reverse_iterator //\n//////////////////////\n\n/*!\n@brief a template for a reverse iterator class\n\n@tparam Base the base iterator type to reverse. Valid types are @ref\niterator (to create @ref reverse_iterator) and @ref const_iterator (to\ncreate @ref const_reverse_iterator).\n\n@requirement The class satisfies the following concept requirements:\n-\n[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator):\n  The iterator that can be moved can be moved in both directions (i.e.\n  incremented and decremented).\n- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator):\n  It is possible to write to the pointed-to element (only if @a Base is\n  @ref iterator).\n\n@since version 1.0.0\n*/\ntemplate<typename Base>\nclass json_reverse_iterator : public std::reverse_iterator<Base>\n{\n  public:\n    using difference_type = std::ptrdiff_t;\n    /// shortcut to the reverse iterator adapter\n    using base_iterator = std::reverse_iterator<Base>;\n    /// the reference type for the pointed-to element\n    using reference = typename Base::reference;\n\n    /// create reverse iterator from iterator\n    explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept\n        : base_iterator(it) {}\n\n    /// create reverse iterator from base class\n    explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {}\n\n    /// post-increment (it++)\n    json_reverse_iterator const operator++(int)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator++(1));\n    }\n\n    /// pre-increment (++it)\n    json_reverse_iterator& operator++()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator++());\n    }\n\n    /// post-decrement (it--)\n    json_reverse_iterator const operator--(int)\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator--(1));\n    }\n\n    /// pre-decrement (--it)\n    json_reverse_iterator& operator--()\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator--());\n    }\n\n    /// add to iterator\n    json_reverse_iterator& operator+=(difference_type i)\n    {\n        return static_cast<json_reverse_iterator&>(base_iterator::operator+=(i));\n    }\n\n    /// add to iterator\n    json_reverse_iterator operator+(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator+(i));\n    }\n\n    /// subtract from iterator\n    json_reverse_iterator operator-(difference_type i) const\n    {\n        return static_cast<json_reverse_iterator>(base_iterator::operator-(i));\n    }\n\n    /// return difference\n    difference_type operator-(const json_reverse_iterator& other) const\n    {\n        return base_iterator(*this) - base_iterator(other);\n    }\n\n    /// access to successor\n    reference operator[](difference_type n) const\n    {\n        return *(this->operator+(n));\n    }\n\n    /// return the key of an object iterator\n    auto key() const -> decltype(std::declval<Base>().key())\n    {\n        auto it = --this->base();\n        return it.key();\n    }\n\n    /// return the value of an iterator\n    reference value() const\n    {\n        auto it = --this->base();\n        return it.operator * ();\n    }\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n\n#include <algorithm> // copy\n#include <cstddef> // size_t\n#include <ios> // streamsize\n#include <iterator> // back_inserter\n#include <memory> // shared_ptr, make_shared\n#include <ostream> // basic_ostream\n#include <string> // basic_string\n#include <vector> // vector\n\nnamespace nlohmann\n{\nnamespace detail\n{\n/// abstract output adapter interface\ntemplate<typename CharType> struct output_adapter_protocol\n{\n    virtual void write_character(CharType c) = 0;\n    virtual void write_characters(const CharType* s, std::size_t length) = 0;\n    virtual ~output_adapter_protocol() = default;\n};\n\n/// a type to simplify interfaces\ntemplate<typename CharType>\nusing output_adapter_t = std::shared_ptr<output_adapter_protocol<CharType>>;\n\n/// output adapter for byte vectors\ntemplate<typename CharType>\nclass output_vector_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_vector_adapter(std::vector<CharType>& vec) noexcept\n        : v(vec)\n    {}\n\n    void write_character(CharType c) override\n    {\n        v.push_back(c);\n    }\n\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        std::copy(s, s + length, std::back_inserter(v));\n    }\n\n  private:\n    std::vector<CharType>& v;\n};\n\n/// output adapter for output streams\ntemplate<typename CharType>\nclass output_stream_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_stream_adapter(std::basic_ostream<CharType>& s) noexcept\n        : stream(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        stream.put(c);\n    }\n\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        stream.write(s, static_cast<std::streamsize>(length));\n    }\n\n  private:\n    std::basic_ostream<CharType>& stream;\n};\n\n/// output adapter for basic_string\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_string_adapter : public output_adapter_protocol<CharType>\n{\n  public:\n    explicit output_string_adapter(StringType& s) noexcept\n        : str(s)\n    {}\n\n    void write_character(CharType c) override\n    {\n        str.push_back(c);\n    }\n\n    void write_characters(const CharType* s, std::size_t length) override\n    {\n        str.append(s, length);\n    }\n\n  private:\n    StringType& str;\n};\n\ntemplate<typename CharType, typename StringType = std::basic_string<CharType>>\nclass output_adapter\n{\n  public:\n    output_adapter(std::vector<CharType>& vec)\n        : oa(std::make_shared<output_vector_adapter<CharType>>(vec)) {}\n\n    output_adapter(std::basic_ostream<CharType>& s)\n        : oa(std::make_shared<output_stream_adapter<CharType>>(s)) {}\n\n    output_adapter(StringType& s)\n        : oa(std::make_shared<output_string_adapter<CharType, StringType>>(s)) {}\n\n    operator output_adapter_t<CharType>()\n    {\n        return oa;\n    }\n\n  private:\n    output_adapter_t<CharType> oa = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/input/binary_reader.hpp>\n\n\n#include <algorithm> // generate_n\n#include <array> // array\n#include <cassert> // assert\n#include <cmath> // ldexp\n#include <cstddef> // size_t\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstdio> // snprintf\n#include <cstring> // memcpy\n#include <iterator> // back_inserter\n#include <limits> // numeric_limits\n#include <string> // char_traits, string\n#include <utility> // make_pair, move\n\n// #include <nlohmann/detail/input/input_adapters.hpp>\n\n// #include <nlohmann/detail/input/json_sax.hpp>\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/is_sax.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////\n// binary reader //\n///////////////////\n\n/*!\n@brief deserialization of CBOR, MessagePack, and UBJSON values\n*/\ntemplate<typename BasicJsonType, typename SAX = json_sax_dom_parser<BasicJsonType>>\nclass binary_reader\n{\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using string_t = typename BasicJsonType::string_t;\n    using json_sax_t = SAX;\n\n  public:\n    /*!\n    @brief create a binary reader\n\n    @param[in] adapter  input adapter to read from\n    */\n    explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter))\n    {\n        (void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};\n        assert(ia);\n    }\n\n    /*!\n    @param[in] format  the binary format to parse\n    @param[in] sax_    a SAX event processor\n    @param[in] strict  whether to expect the input to be consumed completed\n\n    @return\n    */\n    bool sax_parse(const input_format_t format,\n                   json_sax_t* sax_,\n                   const bool strict = true)\n    {\n        sax = sax_;\n        bool result = false;\n\n        switch (format)\n        {\n            case input_format_t::bson:\n                result = parse_bson_internal();\n                break;\n\n            case input_format_t::cbor:\n                result = parse_cbor_internal();\n                break;\n\n            case input_format_t::msgpack:\n                result = parse_msgpack_internal();\n                break;\n\n            case input_format_t::ubjson:\n                result = parse_ubjson_internal();\n                break;\n\n            // LCOV_EXCL_START\n            default:\n                assert(false);\n                // LCOV_EXCL_STOP\n        }\n\n        // strict mode: next byte must be EOF\n        if (result and strict)\n        {\n            if (format == input_format_t::ubjson)\n            {\n                get_ignore_noop();\n            }\n            else\n            {\n                get();\n            }\n\n            if (JSON_UNLIKELY(current != std::char_traits<char>::eof()))\n            {\n                return sax->parse_error(chars_read, get_token_string(),\n                                        parse_error::create(110, chars_read, exception_message(format, \"expected end of input; last byte: 0x\" + get_token_string(), \"value\")));\n            }\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief determine system byte order\n\n    @return true if and only if system's byte order is little endian\n\n    @note from http://stackoverflow.com/a/1001328/266378\n    */\n    static constexpr bool little_endianess(int num = 1) noexcept\n    {\n        return (*reinterpret_cast<char*>(&num) == 1);\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @brief Reads in a BSON-object and passes it to the SAX-parser.\n    @return whether a valid BSON-value was passed to the SAX parser\n    */\n    bool parse_bson_internal()\n    {\n        std::int32_t document_size;\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_UNLIKELY(not sax->start_object(std::size_t(-1))))\n        {\n            return false;\n        }\n\n        if (JSON_UNLIKELY(not parse_bson_element_list(/*is_array*/false)))\n        {\n            return false;\n        }\n\n        return sax->end_object();\n    }\n\n    /*!\n    @brief Parses a C-style string from the BSON input.\n    @param[in, out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @return `true` if the \\x00-byte indicating the end of the string was\n             encountered before the EOF; false` indicates an unexpected EOF.\n    */\n    bool get_bson_cstr(string_t& result)\n    {\n        auto out = std::back_inserter(result);\n        while (true)\n        {\n            get();\n            if (JSON_UNLIKELY(not unexpect_eof(input_format_t::bson, \"cstring\")))\n            {\n                return false;\n            }\n            if (current == 0x00)\n            {\n                return true;\n            }\n            *out++ = static_cast<char>(current);\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief Parses a zero-terminated string of length @a len from the BSON\n           input.\n    @param[in] len  The length (including the zero-byte at the end) of the\n                    string to be read.\n    @param[in, out] result  A reference to the string variable where the read\n                            string is to be stored.\n    @tparam NumberType The type of the length @a len\n    @pre len >= 1\n    @return `true` if the string was successfully parsed\n    */\n    template<typename NumberType>\n    bool get_bson_string(const NumberType len, string_t& result)\n    {\n        if (JSON_UNLIKELY(len < 1))\n        {\n            auto last_token = get_token_string();\n            return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, \"string length must be at least 1, is \" + std::to_string(len), \"string\")));\n        }\n\n        return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) and get() != std::char_traits<char>::eof();\n    }\n\n    /*!\n    @brief Read a BSON document element of the given @a element_type.\n    @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html\n    @param[in] element_type_parse_position The position in the input stream,\n               where the `element_type` was read.\n    @warning Not all BSON element types are supported yet. An unsupported\n             @a element_type will give rise to a parse_error.114:\n             Unsupported BSON record type 0x...\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_internal(const int element_type,\n                                     const std::size_t element_type_parse_position)\n    {\n        switch (element_type)\n        {\n            case 0x01: // double\n            {\n                double number;\n                return get_number<double, true>(input_format_t::bson, number) and sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0x02: // string\n            {\n                std::int32_t len;\n                string_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, len) and get_bson_string(len, value) and sax->string(value);\n            }\n\n            case 0x03: // object\n            {\n                return parse_bson_internal();\n            }\n\n            case 0x04: // array\n            {\n                return parse_bson_array();\n            }\n\n            case 0x08: // boolean\n            {\n                return sax->boolean(static_cast<bool>(get()));\n            }\n\n            case 0x0A: // null\n            {\n                return sax->null();\n            }\n\n            case 0x10: // int32\n            {\n                std::int32_t value;\n                return get_number<std::int32_t, true>(input_format_t::bson, value) and sax->number_integer(value);\n            }\n\n            case 0x12: // int64\n            {\n                std::int64_t value;\n                return get_number<std::int64_t, true>(input_format_t::bson, value) and sax->number_integer(value);\n            }\n\n            default: // anything else not supported (yet)\n            {\n                char cr[3];\n                snprintf(cr, sizeof(cr), \"%.2hhX\", static_cast<unsigned char>(element_type));\n                return sax->parse_error(element_type_parse_position, std::string(cr), parse_error::create(114, element_type_parse_position, \"Unsupported BSON record type 0x\" + std::string(cr)));\n            }\n        }\n    }\n\n    /*!\n    @brief Read a BSON element list (as specified in the BSON-spec)\n\n    The same binary layout is used for objects and arrays, hence it must be\n    indicated with the argument @a is_array which one is expected\n    (true --> array, false --> object).\n\n    @param[in] is_array Determines if the element list being read is to be\n                        treated as an object (@a is_array == false), or as an\n                        array (@a is_array == true).\n    @return whether a valid BSON-object/array was passed to the SAX parser\n    */\n    bool parse_bson_element_list(const bool is_array)\n    {\n        string_t key;\n        while (int element_type = get())\n        {\n            if (JSON_UNLIKELY(not unexpect_eof(input_format_t::bson, \"element list\")))\n            {\n                return false;\n            }\n\n            const std::size_t element_type_parse_position = chars_read;\n            if (JSON_UNLIKELY(not get_bson_cstr(key)))\n            {\n                return false;\n            }\n\n            if (not is_array)\n            {\n                if (not sax->key(key))\n                {\n                    return false;\n                }\n            }\n\n            if (JSON_UNLIKELY(not parse_bson_element_internal(element_type, element_type_parse_position)))\n            {\n                return false;\n            }\n\n            // get_bson_cstr only appends\n            key.clear();\n        }\n\n        return true;\n    }\n\n    /*!\n    @brief Reads an array from the BSON input and passes it to the SAX-parser.\n    @return whether a valid BSON-array was passed to the SAX parser\n    */\n    bool parse_bson_array()\n    {\n        std::int32_t document_size;\n        get_number<std::int32_t, true>(input_format_t::bson, document_size);\n\n        if (JSON_UNLIKELY(not sax->start_array(std::size_t(-1))))\n        {\n            return false;\n        }\n\n        if (JSON_UNLIKELY(not parse_bson_element_list(/*is_array*/true)))\n        {\n            return false;\n        }\n\n        return sax->end_array();\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether a valid CBOR value was passed to the SAX parser\n    */\n    bool parse_cbor_internal(const bool get_char = true)\n    {\n        switch (get_char ? get() : current)\n        {\n            // EOF\n            case std::char_traits<char>::eof():\n                return unexpect_eof(input_format_t::cbor, \"value\");\n\n            // Integer 0x00..0x17 (0..23)\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            case 0x18: // Unsigned integer (one-byte uint8_t follows)\n            {\n                uint8_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);\n            }\n\n            case 0x19: // Unsigned integer (two-byte uint16_t follows)\n            {\n                uint16_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);\n            }\n\n            case 0x1A: // Unsigned integer (four-byte uint32_t follows)\n            {\n                uint32_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);\n            }\n\n            case 0x1B: // Unsigned integer (eight-byte uint64_t follows)\n            {\n                uint64_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_unsigned(number);\n            }\n\n            // Negative integer -1-0x00..-1-0x17 (-1..-24)\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n                return sax->number_integer(static_cast<int8_t>(0x20 - 1 - current));\n\n            case 0x38: // Negative integer (one-byte uint8_t follows)\n            {\n                uint8_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x39: // Negative integer -1-n (two-byte uint16_t follows)\n            {\n                uint16_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3A: // Negative integer -1-n (four-byte uint32_t follows)\n            {\n                uint32_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1) - number);\n            }\n\n            case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows)\n            {\n                uint64_t number;\n                return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast<number_integer_t>(-1)\n                        - static_cast<number_integer_t>(number));\n            }\n\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                string_t s;\n                return get_cbor_string(s) and sax->string(s);\n            }\n\n            // array (0x00..0x17 data items follow)\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n                return get_cbor_array(static_cast<std::size_t>(current & 0x1F));\n\n            case 0x98: // array (one-byte uint8_t for n follows)\n            {\n                uint8_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));\n            }\n\n            case 0x99: // array (two-byte uint16_t for n follow)\n            {\n                uint16_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));\n            }\n\n            case 0x9A: // array (four-byte uint32_t for n follow)\n            {\n                uint32_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));\n            }\n\n            case 0x9B: // array (eight-byte uint64_t for n follow)\n            {\n                uint64_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast<std::size_t>(len));\n            }\n\n            case 0x9F: // array (indefinite length)\n                return get_cbor_array(std::size_t(-1));\n\n            // map (0x00..0x17 pairs of data items follow)\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n                return get_cbor_object(static_cast<std::size_t>(current & 0x1F));\n\n            case 0xB8: // map (one-byte uint8_t for n follows)\n            {\n                uint8_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xB9: // map (two-byte uint16_t for n follow)\n            {\n                uint16_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xBA: // map (four-byte uint32_t for n follow)\n            {\n                uint32_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xBB: // map (eight-byte uint64_t for n follow)\n            {\n                uint64_t len;\n                return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xBF: // map (indefinite length)\n                return get_cbor_object(std::size_t(-1));\n\n            case 0xF4: // false\n                return sax->boolean(false);\n\n            case 0xF5: // true\n                return sax->boolean(true);\n\n            case 0xF6: // null\n                return sax->null();\n\n            case 0xF9: // Half-Precision Float (two-byte IEEE 754)\n            {\n                const int byte1_raw = get();\n                if (JSON_UNLIKELY(not unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n                const int byte2_raw = get();\n                if (JSON_UNLIKELY(not unexpect_eof(input_format_t::cbor, \"number\")))\n                {\n                    return false;\n                }\n\n                const auto byte1 = static_cast<unsigned char>(byte1_raw);\n                const auto byte2 = static_cast<unsigned char>(byte2_raw);\n\n                // code from RFC 7049, Appendix D, Figure 3:\n                // As half-precision floating-point numbers were only added\n                // to IEEE 754 in 2008, today's programming platforms often\n                // still only have limited support for them. It is very\n                // easy to include at least decoding support for them even\n                // without such support. An example of a small decoder for\n                // half-precision floating-point numbers in the C language\n                // is shown in Fig. 3.\n                const int half = (byte1 << 8) + byte2;\n                const double val = [&half]\n                {\n                    const int exp = (half >> 10) & 0x1F;\n                    const int mant = half & 0x3FF;\n                    assert(0 <= exp and exp <= 32);\n                    assert(0 <= mant and mant <= 1024);\n                    switch (exp)\n                    {\n                        case 0:\n                            return std::ldexp(mant, -24);\n                        case 31:\n                            return (mant == 0)\n                            ? std::numeric_limits<double>::infinity()\n                            : std::numeric_limits<double>::quiet_NaN();\n                        default:\n                            return std::ldexp(mant + 1024, exp - 25);\n                    }\n                }();\n                return sax->number_float((half & 0x8000) != 0\n                                         ? static_cast<number_float_t>(-val)\n                                         : static_cast<number_float_t>(val), \"\");\n            }\n\n            case 0xFA: // Single-Precision Float (four-byte IEEE 754)\n            {\n                float number;\n                return get_number(input_format_t::cbor, number) and sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xFB: // Double-Precision Float (eight-byte IEEE 754)\n            {\n                double number;\n                return get_number(input_format_t::cbor, number) and sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            default: // anything else (0xFF is handled inside the other types)\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, \"invalid byte: 0x\" + last_token, \"value\")));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a CBOR string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n    Additionally, CBOR's strings with indefinite lengths are supported.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_cbor_string(string_t& result)\n    {\n        if (JSON_UNLIKELY(not unexpect_eof(input_format_t::cbor, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // UTF-8 string (0x00..0x17 bytes follow)\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            {\n                return get_string(input_format_t::cbor, current & 0x1F, result);\n            }\n\n            case 0x78: // UTF-8 string (one-byte uint8_t for n follows)\n            {\n                uint8_t len;\n                return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x79: // UTF-8 string (two-byte uint16_t for n follow)\n            {\n                uint16_t len;\n                return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7A: // UTF-8 string (four-byte uint32_t for n follow)\n            {\n                uint32_t len;\n                return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow)\n            {\n                uint64_t len;\n                return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result);\n            }\n\n            case 0x7F: // UTF-8 string (indefinite length)\n            {\n                while (get() != 0xFF)\n                {\n                    string_t chunk;\n                    if (not get_cbor_string(chunk))\n                    {\n                        return false;\n                    }\n                    result.append(chunk);\n                }\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, \"expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x\" + last_token, \"string\")));\n            }\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array or std::size_t(-1) for an\n                    array of indefinite size\n    @return whether array creation completed\n    */\n    bool get_cbor_array(const std::size_t len)\n    {\n        if (JSON_UNLIKELY(not sax->start_array(len)))\n        {\n            return false;\n        }\n\n        if (len != std::size_t(-1))\n        {\n            for (std::size_t i = 0; i < len; ++i)\n            {\n                if (JSON_UNLIKELY(not parse_cbor_internal()))\n                {\n                    return false;\n                }\n            }\n        }\n        else\n        {\n            while (get() != 0xFF)\n            {\n                if (JSON_UNLIKELY(not parse_cbor_internal(false)))\n                {\n                    return false;\n                }\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object or std::size_t(-1) for an\n                    object of indefinite size\n    @return whether object creation completed\n    */\n    bool get_cbor_object(const std::size_t len)\n    {\n        if (not JSON_UNLIKELY(sax->start_object(len)))\n        {\n            return false;\n        }\n\n        string_t key;\n        if (len != std::size_t(-1))\n        {\n            for (std::size_t i = 0; i < len; ++i)\n            {\n                get();\n                if (JSON_UNLIKELY(not get_cbor_string(key) or not sax->key(key)))\n                {\n                    return false;\n                }\n\n                if (JSON_UNLIKELY(not parse_cbor_internal()))\n                {\n                    return false;\n                }\n                key.clear();\n            }\n        }\n        else\n        {\n            while (get() != 0xFF)\n            {\n                if (JSON_UNLIKELY(not get_cbor_string(key) or not sax->key(key)))\n                {\n                    return false;\n                }\n\n                if (JSON_UNLIKELY(not parse_cbor_internal()))\n                {\n                    return false;\n                }\n                key.clear();\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    /*!\n    @return whether a valid MessagePack value was passed to the SAX parser\n    */\n    bool parse_msgpack_internal()\n    {\n        switch (get())\n        {\n            // EOF\n            case std::char_traits<char>::eof():\n                return unexpect_eof(input_format_t::msgpack, \"value\");\n\n            // positive fixint\n            case 0x00:\n            case 0x01:\n            case 0x02:\n            case 0x03:\n            case 0x04:\n            case 0x05:\n            case 0x06:\n            case 0x07:\n            case 0x08:\n            case 0x09:\n            case 0x0A:\n            case 0x0B:\n            case 0x0C:\n            case 0x0D:\n            case 0x0E:\n            case 0x0F:\n            case 0x10:\n            case 0x11:\n            case 0x12:\n            case 0x13:\n            case 0x14:\n            case 0x15:\n            case 0x16:\n            case 0x17:\n            case 0x18:\n            case 0x19:\n            case 0x1A:\n            case 0x1B:\n            case 0x1C:\n            case 0x1D:\n            case 0x1E:\n            case 0x1F:\n            case 0x20:\n            case 0x21:\n            case 0x22:\n            case 0x23:\n            case 0x24:\n            case 0x25:\n            case 0x26:\n            case 0x27:\n            case 0x28:\n            case 0x29:\n            case 0x2A:\n            case 0x2B:\n            case 0x2C:\n            case 0x2D:\n            case 0x2E:\n            case 0x2F:\n            case 0x30:\n            case 0x31:\n            case 0x32:\n            case 0x33:\n            case 0x34:\n            case 0x35:\n            case 0x36:\n            case 0x37:\n            case 0x38:\n            case 0x39:\n            case 0x3A:\n            case 0x3B:\n            case 0x3C:\n            case 0x3D:\n            case 0x3E:\n            case 0x3F:\n            case 0x40:\n            case 0x41:\n            case 0x42:\n            case 0x43:\n            case 0x44:\n            case 0x45:\n            case 0x46:\n            case 0x47:\n            case 0x48:\n            case 0x49:\n            case 0x4A:\n            case 0x4B:\n            case 0x4C:\n            case 0x4D:\n            case 0x4E:\n            case 0x4F:\n            case 0x50:\n            case 0x51:\n            case 0x52:\n            case 0x53:\n            case 0x54:\n            case 0x55:\n            case 0x56:\n            case 0x57:\n            case 0x58:\n            case 0x59:\n            case 0x5A:\n            case 0x5B:\n            case 0x5C:\n            case 0x5D:\n            case 0x5E:\n            case 0x5F:\n            case 0x60:\n            case 0x61:\n            case 0x62:\n            case 0x63:\n            case 0x64:\n            case 0x65:\n            case 0x66:\n            case 0x67:\n            case 0x68:\n            case 0x69:\n            case 0x6A:\n            case 0x6B:\n            case 0x6C:\n            case 0x6D:\n            case 0x6E:\n            case 0x6F:\n            case 0x70:\n            case 0x71:\n            case 0x72:\n            case 0x73:\n            case 0x74:\n            case 0x75:\n            case 0x76:\n            case 0x77:\n            case 0x78:\n            case 0x79:\n            case 0x7A:\n            case 0x7B:\n            case 0x7C:\n            case 0x7D:\n            case 0x7E:\n            case 0x7F:\n                return sax->number_unsigned(static_cast<number_unsigned_t>(current));\n\n            // fixmap\n            case 0x80:\n            case 0x81:\n            case 0x82:\n            case 0x83:\n            case 0x84:\n            case 0x85:\n            case 0x86:\n            case 0x87:\n            case 0x88:\n            case 0x89:\n            case 0x8A:\n            case 0x8B:\n            case 0x8C:\n            case 0x8D:\n            case 0x8E:\n            case 0x8F:\n                return get_msgpack_object(static_cast<std::size_t>(current & 0x0F));\n\n            // fixarray\n            case 0x90:\n            case 0x91:\n            case 0x92:\n            case 0x93:\n            case 0x94:\n            case 0x95:\n            case 0x96:\n            case 0x97:\n            case 0x98:\n            case 0x99:\n            case 0x9A:\n            case 0x9B:\n            case 0x9C:\n            case 0x9D:\n            case 0x9E:\n            case 0x9F:\n                return get_msgpack_array(static_cast<std::size_t>(current & 0x0F));\n\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            {\n                string_t s;\n                return get_msgpack_string(s) and sax->string(s);\n            }\n\n            case 0xC0: // nil\n                return sax->null();\n\n            case 0xC2: // false\n                return sax->boolean(false);\n\n            case 0xC3: // true\n                return sax->boolean(true);\n\n            case 0xCA: // float 32\n            {\n                float number;\n                return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCB: // float 64\n            {\n                double number;\n                return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 0xCC: // uint 8\n            {\n                uint8_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);\n            }\n\n            case 0xCD: // uint 16\n            {\n                uint16_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);\n            }\n\n            case 0xCE: // uint 32\n            {\n                uint32_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);\n            }\n\n            case 0xCF: // uint 64\n            {\n                uint64_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number);\n            }\n\n            case 0xD0: // int 8\n            {\n                int8_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_integer(number);\n            }\n\n            case 0xD1: // int 16\n            {\n                int16_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_integer(number);\n            }\n\n            case 0xD2: // int 32\n            {\n                int32_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_integer(number);\n            }\n\n            case 0xD3: // int 64\n            {\n                int64_t number;\n                return get_number(input_format_t::msgpack, number) and sax->number_integer(number);\n            }\n\n            case 0xD9: // str 8\n            case 0xDA: // str 16\n            case 0xDB: // str 32\n            {\n                string_t s;\n                return get_msgpack_string(s) and sax->string(s);\n            }\n\n            case 0xDC: // array 16\n            {\n                uint16_t len;\n                return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast<std::size_t>(len));\n            }\n\n            case 0xDD: // array 32\n            {\n                uint32_t len;\n                return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast<std::size_t>(len));\n            }\n\n            case 0xDE: // map 16\n            {\n                uint16_t len;\n                return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast<std::size_t>(len));\n            }\n\n            case 0xDF: // map 32\n            {\n                uint32_t len;\n                return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast<std::size_t>(len));\n            }\n\n            // negative fixint\n            case 0xE0:\n            case 0xE1:\n            case 0xE2:\n            case 0xE3:\n            case 0xE4:\n            case 0xE5:\n            case 0xE6:\n            case 0xE7:\n            case 0xE8:\n            case 0xE9:\n            case 0xEA:\n            case 0xEB:\n            case 0xEC:\n            case 0xED:\n            case 0xEE:\n            case 0xEF:\n            case 0xF0:\n            case 0xF1:\n            case 0xF2:\n            case 0xF3:\n            case 0xF4:\n            case 0xF5:\n            case 0xF6:\n            case 0xF7:\n            case 0xF8:\n            case 0xF9:\n            case 0xFA:\n            case 0xFB:\n            case 0xFC:\n            case 0xFD:\n            case 0xFE:\n            case 0xFF:\n                return sax->number_integer(static_cast<int8_t>(current));\n\n            default: // anything else\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, \"invalid byte: 0x\" + last_token, \"value\")));\n            }\n        }\n    }\n\n    /*!\n    @brief reads a MessagePack string\n\n    This function first reads starting bytes to determine the expected\n    string length and then copies this number of bytes into a string.\n\n    @param[out] result  created string\n\n    @return whether string creation completed\n    */\n    bool get_msgpack_string(string_t& result)\n    {\n        if (JSON_UNLIKELY(not unexpect_eof(input_format_t::msgpack, \"string\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            // fixstr\n            case 0xA0:\n            case 0xA1:\n            case 0xA2:\n            case 0xA3:\n            case 0xA4:\n            case 0xA5:\n            case 0xA6:\n            case 0xA7:\n            case 0xA8:\n            case 0xA9:\n            case 0xAA:\n            case 0xAB:\n            case 0xAC:\n            case 0xAD:\n            case 0xAE:\n            case 0xAF:\n            case 0xB0:\n            case 0xB1:\n            case 0xB2:\n            case 0xB3:\n            case 0xB4:\n            case 0xB5:\n            case 0xB6:\n            case 0xB7:\n            case 0xB8:\n            case 0xB9:\n            case 0xBA:\n            case 0xBB:\n            case 0xBC:\n            case 0xBD:\n            case 0xBE:\n            case 0xBF:\n            {\n                return get_string(input_format_t::msgpack, current & 0x1F, result);\n            }\n\n            case 0xD9: // str 8\n            {\n                uint8_t len;\n                return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDA: // str 16\n            {\n                uint16_t len;\n                return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result);\n            }\n\n            case 0xDB: // str 32\n            {\n                uint32_t len;\n                return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result);\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, \"expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x\" + last_token, \"string\")));\n            }\n        }\n    }\n\n    /*!\n    @param[in] len  the length of the array\n    @return whether array creation completed\n    */\n    bool get_msgpack_array(const std::size_t len)\n    {\n        if (JSON_UNLIKELY(not sax->start_array(len)))\n        {\n            return false;\n        }\n\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            if (JSON_UNLIKELY(not parse_msgpack_internal()))\n            {\n                return false;\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @param[in] len  the length of the object\n    @return whether object creation completed\n    */\n    bool get_msgpack_object(const std::size_t len)\n    {\n        if (JSON_UNLIKELY(not sax->start_object(len)))\n        {\n            return false;\n        }\n\n        string_t key;\n        for (std::size_t i = 0; i < len; ++i)\n        {\n            get();\n            if (JSON_UNLIKELY(not get_msgpack_string(key) or not sax->key(key)))\n            {\n                return false;\n            }\n\n            if (JSON_UNLIKELY(not parse_msgpack_internal()))\n            {\n                return false;\n            }\n            key.clear();\n        }\n\n        return sax->end_object();\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    /*!\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether a valid UBJSON value was passed to the SAX parser\n    */\n    bool parse_ubjson_internal(const bool get_char = true)\n    {\n        return get_ubjson_value(get_char ? get_ignore_noop() : current);\n    }\n\n    /*!\n    @brief reads a UBJSON string\n\n    This function is either called after reading the 'S' byte explicitly\n    indicating a string, or in case of an object key where the 'S' byte can be\n    left out.\n\n    @param[out] result   created string\n    @param[in] get_char  whether a new character should be retrieved from the\n                         input (true, default) or whether the last read\n                         character should be considered instead\n\n    @return whether string creation completed\n    */\n    bool get_ubjson_string(string_t& result, const bool get_char = true)\n    {\n        if (get_char)\n        {\n            get();  // TODO: may we ignore N here?\n        }\n\n        if (JSON_UNLIKELY(not unexpect_eof(input_format_t::ubjson, \"value\")))\n        {\n            return false;\n        }\n\n        switch (current)\n        {\n            case 'U':\n            {\n                uint8_t len;\n                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'i':\n            {\n                int8_t len;\n                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'I':\n            {\n                int16_t len;\n                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'l':\n            {\n                int32_t len;\n                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);\n            }\n\n            case 'L':\n            {\n                int64_t len;\n                return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result);\n            }\n\n            default:\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"expected length type specification (U, i, I, l, L); last byte: 0x\" + last_token, \"string\")));\n        }\n    }\n\n    /*!\n    @param[out] result  determined size\n    @return whether size determination completed\n    */\n    bool get_ubjson_size_value(std::size_t& result)\n    {\n        switch (get_ignore_noop())\n        {\n            case 'U':\n            {\n                uint8_t number;\n                if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'i':\n            {\n                int8_t number;\n                if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'I':\n            {\n                int16_t number;\n                if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'l':\n            {\n                int32_t number;\n                if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            case 'L':\n            {\n                int64_t number;\n                if (JSON_UNLIKELY(not get_number(input_format_t::ubjson, number)))\n                {\n                    return false;\n                }\n                result = static_cast<std::size_t>(number);\n                return true;\n            }\n\n            default:\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"expected length type specification (U, i, I, l, L) after '#'; last byte: 0x\" + last_token, \"size\")));\n            }\n        }\n    }\n\n    /*!\n    @brief determine the type and size for a container\n\n    In the optimized UBJSON format, a type and a size can be provided to allow\n    for a more compact representation.\n\n    @param[out] result  pair of the size and the type\n\n    @return whether pair creation completed\n    */\n    bool get_ubjson_size_type(std::pair<std::size_t, int>& result)\n    {\n        result.first = string_t::npos; // size\n        result.second = 0; // type\n\n        get_ignore_noop();\n\n        if (current == '$')\n        {\n            result.second = get();  // must not ignore 'N', because 'N' maybe the type\n            if (JSON_UNLIKELY(not unexpect_eof(input_format_t::ubjson, \"type\")))\n            {\n                return false;\n            }\n\n            get_ignore_noop();\n            if (JSON_UNLIKELY(current != '#'))\n            {\n                if (JSON_UNLIKELY(not unexpect_eof(input_format_t::ubjson, \"value\")))\n                {\n                    return false;\n                }\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, \"expected '#' after type information; last byte: 0x\" + last_token, \"size\")));\n            }\n\n            return get_ubjson_size_value(result.first);\n        }\n        else if (current == '#')\n        {\n            return get_ubjson_size_value(result.first);\n        }\n        return true;\n    }\n\n    /*!\n    @param prefix  the previously read or set type prefix\n    @return whether value creation completed\n    */\n    bool get_ubjson_value(const int prefix)\n    {\n        switch (prefix)\n        {\n            case std::char_traits<char>::eof():  // EOF\n                return unexpect_eof(input_format_t::ubjson, \"value\");\n\n            case 'T':  // true\n                return sax->boolean(true);\n            case 'F':  // false\n                return sax->boolean(false);\n\n            case 'Z':  // null\n                return sax->null();\n\n            case 'U':\n            {\n                uint8_t number;\n                return get_number(input_format_t::ubjson, number) and sax->number_unsigned(number);\n            }\n\n            case 'i':\n            {\n                int8_t number;\n                return get_number(input_format_t::ubjson, number) and sax->number_integer(number);\n            }\n\n            case 'I':\n            {\n                int16_t number;\n                return get_number(input_format_t::ubjson, number) and sax->number_integer(number);\n            }\n\n            case 'l':\n            {\n                int32_t number;\n                return get_number(input_format_t::ubjson, number) and sax->number_integer(number);\n            }\n\n            case 'L':\n            {\n                int64_t number;\n                return get_number(input_format_t::ubjson, number) and sax->number_integer(number);\n            }\n\n            case 'd':\n            {\n                float number;\n                return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'D':\n            {\n                double number;\n                return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast<number_float_t>(number), \"\");\n            }\n\n            case 'C':  // char\n            {\n                get();\n                if (JSON_UNLIKELY(not unexpect_eof(input_format_t::ubjson, \"char\")))\n                {\n                    return false;\n                }\n                if (JSON_UNLIKELY(current > 127))\n                {\n                    auto last_token = get_token_string();\n                    return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, \"byte after 'C' must be in range 0x00..0x7F; last byte: 0x\" + last_token, \"char\")));\n                }\n                string_t s(1, static_cast<char>(current));\n                return sax->string(s);\n            }\n\n            case 'S':  // string\n            {\n                string_t s;\n                return get_ubjson_string(s) and sax->string(s);\n            }\n\n            case '[':  // array\n                return get_ubjson_array();\n\n            case '{':  // object\n                return get_ubjson_object();\n\n            default: // anything else\n            {\n                auto last_token = get_token_string();\n                return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, \"invalid byte: 0x\" + last_token, \"value\")));\n            }\n        }\n    }\n\n    /*!\n    @return whether array creation completed\n    */\n    bool get_ubjson_array()\n    {\n        std::pair<std::size_t, int> size_and_type;\n        if (JSON_UNLIKELY(not get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        if (size_and_type.first != string_t::npos)\n        {\n            if (JSON_UNLIKELY(not sax->start_array(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                if (size_and_type.second != 'N')\n                {\n                    for (std::size_t i = 0; i < size_and_type.first; ++i)\n                    {\n                        if (JSON_UNLIKELY(not get_ubjson_value(size_and_type.second)))\n                        {\n                            return false;\n                        }\n                    }\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_UNLIKELY(not parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                }\n            }\n        }\n        else\n        {\n            if (JSON_UNLIKELY(not sax->start_array(std::size_t(-1))))\n            {\n                return false;\n            }\n\n            while (current != ']')\n            {\n                if (JSON_UNLIKELY(not parse_ubjson_internal(false)))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n            }\n        }\n\n        return sax->end_array();\n    }\n\n    /*!\n    @return whether object creation completed\n    */\n    bool get_ubjson_object()\n    {\n        std::pair<std::size_t, int> size_and_type;\n        if (JSON_UNLIKELY(not get_ubjson_size_type(size_and_type)))\n        {\n            return false;\n        }\n\n        string_t key;\n        if (size_and_type.first != string_t::npos)\n        {\n            if (JSON_UNLIKELY(not sax->start_object(size_and_type.first)))\n            {\n                return false;\n            }\n\n            if (size_and_type.second != 0)\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_UNLIKELY(not get_ubjson_string(key) or not sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_UNLIKELY(not get_ubjson_value(size_and_type.second)))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n            else\n            {\n                for (std::size_t i = 0; i < size_and_type.first; ++i)\n                {\n                    if (JSON_UNLIKELY(not get_ubjson_string(key) or not sax->key(key)))\n                    {\n                        return false;\n                    }\n                    if (JSON_UNLIKELY(not parse_ubjson_internal()))\n                    {\n                        return false;\n                    }\n                    key.clear();\n                }\n            }\n        }\n        else\n        {\n            if (JSON_UNLIKELY(not sax->start_object(std::size_t(-1))))\n            {\n                return false;\n            }\n\n            while (current != '}')\n            {\n                if (JSON_UNLIKELY(not get_ubjson_string(key, false) or not sax->key(key)))\n                {\n                    return false;\n                }\n                if (JSON_UNLIKELY(not parse_ubjson_internal()))\n                {\n                    return false;\n                }\n                get_ignore_noop();\n                key.clear();\n            }\n        }\n\n        return sax->end_object();\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*!\n    @brief get next character from the input\n\n    This function provides the interface to the used input adapter. It does\n    not throw in case the input reached EOF, but returns a -'ve valued\n    `std::char_traits<char>::eof()` in that case.\n\n    @return character read from the input\n    */\n    int get()\n    {\n        ++chars_read;\n        return (current = ia->get_character());\n    }\n\n    /*!\n    @return character read from the input after ignoring all 'N' entries\n    */\n    int get_ignore_noop()\n    {\n        do\n        {\n            get();\n        }\n        while (current == 'N');\n\n        return current;\n    }\n\n    /*\n    @brief read a number from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format   the current format (for diagnostics)\n    @param[out] result  number of type @a NumberType\n\n    @return whether conversion completed\n\n    @note This function needs to respect the system's endianess, because\n          bytes in CBOR, MessagePack, and UBJSON are stored in network order\n          (big endian) and therefore need reordering on little endian systems.\n    */\n    template<typename NumberType, bool InputIsLittleEndian = false>\n    bool get_number(const input_format_t format, NumberType& result)\n    {\n        // step 1: read input into array with system's byte order\n        std::array<uint8_t, sizeof(NumberType)> vec;\n        for (std::size_t i = 0; i < sizeof(NumberType); ++i)\n        {\n            get();\n            if (JSON_UNLIKELY(not unexpect_eof(format, \"number\")))\n            {\n                return false;\n            }\n\n            // reverse byte order prior to conversion if necessary\n            if (is_little_endian && !InputIsLittleEndian)\n            {\n                vec[sizeof(NumberType) - i - 1] = static_cast<uint8_t>(current);\n            }\n            else\n            {\n                vec[i] = static_cast<uint8_t>(current); // LCOV_EXCL_LINE\n            }\n        }\n\n        // step 2: convert array into number of type T and return\n        std::memcpy(&result, vec.data(), sizeof(NumberType));\n        return true;\n    }\n\n    /*!\n    @brief create a string by reading characters from the input\n\n    @tparam NumberType the type of the number\n    @param[in] format the current format (for diagnostics)\n    @param[in] len number of characters to read\n    @param[out] result string created by reading @a len bytes\n\n    @return whether string creation completed\n\n    @note We can not reserve @a len bytes for the result, because @a len\n          may be too large. Usually, @ref unexpect_eof() detects the end of\n          the input before we run out of string memory.\n    */\n    template<typename NumberType>\n    bool get_string(const input_format_t format,\n                    const NumberType len,\n                    string_t& result)\n    {\n        bool success = true;\n        std::generate_n(std::back_inserter(result), len, [this, &success, &format]()\n        {\n            get();\n            if (JSON_UNLIKELY(not unexpect_eof(format, \"string\")))\n            {\n                success = false;\n            }\n            return static_cast<char>(current);\n        });\n        return success;\n    }\n\n    /*!\n    @param[in] format   the current format (for diagnostics)\n    @param[in] context  further context information (for diagnostics)\n    @return whether the last read character is not EOF\n    */\n    bool unexpect_eof(const input_format_t format, const char* context) const\n    {\n        if (JSON_UNLIKELY(current == std::char_traits<char>::eof()))\n        {\n            return sax->parse_error(chars_read, \"<end of file>\",\n                                    parse_error::create(110, chars_read, exception_message(format, \"unexpected end of input\", context)));\n        }\n        return true;\n    }\n\n    /*!\n    @return a string representation of the last read byte\n    */\n    std::string get_token_string() const\n    {\n        char cr[3];\n        snprintf(cr, 3, \"%.2hhX\", static_cast<unsigned char>(current));\n        return std::string{cr};\n    }\n\n    /*!\n    @param[in] format   the current format\n    @param[in] detail   a detailed error message\n    @param[in] context  further contect information\n    @return a message string to use in the parse_error exceptions\n    */\n    std::string exception_message(const input_format_t format,\n                                  const std::string& detail,\n                                  const std::string& context) const\n    {\n        std::string error_msg = \"syntax error while parsing \";\n\n        switch (format)\n        {\n            case input_format_t::cbor:\n                error_msg += \"CBOR\";\n                break;\n\n            case input_format_t::msgpack:\n                error_msg += \"MessagePack\";\n                break;\n\n            case input_format_t::ubjson:\n                error_msg += \"UBJSON\";\n                break;\n\n            case input_format_t::bson:\n                error_msg += \"BSON\";\n                break;\n\n            // LCOV_EXCL_START\n            default:\n                assert(false);\n                // LCOV_EXCL_STOP\n        }\n\n        return error_msg + \" \" + context + \": \" + detail;\n    }\n\n  private:\n    /// input adapter\n    input_adapter_t ia = nullptr;\n\n    /// the current character\n    int current = std::char_traits<char>::eof();\n\n    /// the number of characters read\n    std::size_t chars_read = 0;\n\n    /// whether we can assume little endianess\n    const bool is_little_endian = little_endianess();\n\n    /// the SAX parser\n    json_sax_t* sax = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/output/binary_writer.hpp>\n\n\n#include <algorithm> // reverse\n#include <array> // array\n#include <cstdint> // uint8_t, uint16_t, uint32_t, uint64_t\n#include <cstring> // memcpy\n#include <limits> // numeric_limits\n\n// #include <nlohmann/detail/input/binary_reader.hpp>\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////\n// binary writer //\n///////////////////\n\n/*!\n@brief serialization to CBOR and MessagePack values\n*/\ntemplate<typename BasicJsonType, typename CharType>\nclass binary_writer\n{\n    using string_t = typename BasicJsonType::string_t;\n\n  public:\n    /*!\n    @brief create a binary writer\n\n    @param[in] adapter  output adapter to write to\n    */\n    explicit binary_writer(output_adapter_t<CharType> adapter) : oa(adapter)\n    {\n        assert(oa);\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    void write_bson(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::object:\n            {\n                write_bson_object(*j.m_value.object);\n                break;\n            }\n\n            default:\n            {\n                JSON_THROW(type_error::create(317, \"to serialize to BSON, top-level type must be object, but is \" + std::string(j.type_name())));\n            }\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    */\n    void write_cbor(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n            {\n                oa->write_character(to_char_type(0xF6));\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                oa->write_character(j.m_value.boolean\n                                    ? to_char_type(0xF5)\n                                    : to_char_type(0xF4));\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                if (j.m_value.number_integer >= 0)\n                {\n                    // CBOR does not differentiate between positive signed\n                    // integers and unsigned integers. Therefore, we used the\n                    // code from the value_t::number_unsigned case here.\n                    if (j.m_value.number_integer <= 0x17)\n                    {\n                        write_number(static_cast<uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x18));\n                        write_number(static_cast<uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<uint16_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x19));\n                        write_number(static_cast<uint16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer <= (std::numeric_limits<uint32_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x1A));\n                        write_number(static_cast<uint32_t>(j.m_value.number_integer));\n                    }\n                    else\n                    {\n                        oa->write_character(to_char_type(0x1B));\n                        write_number(static_cast<uint64_t>(j.m_value.number_integer));\n                    }\n                }\n                else\n                {\n                    // The conversions below encode the sign in the first\n                    // byte, and the value is converted to a positive number.\n                    const auto positive_number = -1 - j.m_value.number_integer;\n                    if (j.m_value.number_integer >= -24)\n                    {\n                        write_number(static_cast<uint8_t>(0x20 + positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<uint8_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x38));\n                        write_number(static_cast<uint8_t>(positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<uint16_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x39));\n                        write_number(static_cast<uint16_t>(positive_number));\n                    }\n                    else if (positive_number <= (std::numeric_limits<uint32_t>::max)())\n                    {\n                        oa->write_character(to_char_type(0x3A));\n                        write_number(static_cast<uint32_t>(positive_number));\n                    }\n                    else\n                    {\n                        oa->write_character(to_char_type(0x3B));\n                        write_number(static_cast<uint64_t>(positive_number));\n                    }\n                }\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned <= 0x17)\n                {\n                    write_number(static_cast<uint8_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x18));\n                    write_number(static_cast<uint8_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x19));\n                    write_number(static_cast<uint16_t>(j.m_value.number_unsigned));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x1A));\n                    write_number(static_cast<uint32_t>(j.m_value.number_unsigned));\n                }\n                else\n                {\n                    oa->write_character(to_char_type(0x1B));\n                    write_number(static_cast<uint64_t>(j.m_value.number_unsigned));\n                }\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                oa->write_character(get_cbor_float_prefix(j.m_value.number_float));\n                write_number(j.m_value.number_float);\n                break;\n            }\n\n            case value_t::string:\n            {\n                // step 1: write control byte and the string length\n                const auto N = j.m_value.string->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<uint8_t>(0x60 + N));\n                }\n                else if (N <= (std::numeric_limits<uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x78));\n                    write_number(static_cast<uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x79));\n                    write_number(static_cast<uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x7A));\n                    write_number(static_cast<uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x7B));\n                    write_number(static_cast<uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write the string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                // step 1: write control byte and the array size\n                const auto N = j.m_value.array->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<uint8_t>(0x80 + N));\n                }\n                else if (N <= (std::numeric_limits<uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x98));\n                    write_number(static_cast<uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x99));\n                    write_number(static_cast<uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x9A));\n                    write_number(static_cast<uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0x9B));\n                    write_number(static_cast<uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_cbor(el);\n                }\n                break;\n            }\n\n            case value_t::object:\n            {\n                // step 1: write control byte and the object size\n                const auto N = j.m_value.object->size();\n                if (N <= 0x17)\n                {\n                    write_number(static_cast<uint8_t>(0xA0 + N));\n                }\n                else if (N <= (std::numeric_limits<uint8_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xB8));\n                    write_number(static_cast<uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<uint16_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xB9));\n                    write_number(static_cast<uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<uint32_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xBA));\n                    write_number(static_cast<uint32_t>(N));\n                }\n                // LCOV_EXCL_START\n                else if (N <= (std::numeric_limits<uint64_t>::max)())\n                {\n                    oa->write_character(to_char_type(0xBB));\n                    write_number(static_cast<uint64_t>(N));\n                }\n                // LCOV_EXCL_STOP\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_cbor(el.first);\n                    write_cbor(el.second);\n                }\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    */\n    void write_msgpack(const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::null: // nil\n            {\n                oa->write_character(to_char_type(0xC0));\n                break;\n            }\n\n            case value_t::boolean: // true and false\n            {\n                oa->write_character(j.m_value.boolean\n                                    ? to_char_type(0xC3)\n                                    : to_char_type(0xC2));\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                if (j.m_value.number_integer >= 0)\n                {\n                    // MessagePack does not differentiate between positive\n                    // signed integers and unsigned integers. Therefore, we used\n                    // the code from the value_t::number_unsigned case here.\n                    if (j.m_value.number_unsigned < 128)\n                    {\n                        // positive fixnum\n                        write_number(static_cast<uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())\n                    {\n                        // uint 8\n                        oa->write_character(to_char_type(0xCC));\n                        write_number(static_cast<uint8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())\n                    {\n                        // uint 16\n                        oa->write_character(to_char_type(0xCD));\n                        write_number(static_cast<uint16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())\n                    {\n                        // uint 32\n                        oa->write_character(to_char_type(0xCE));\n                        write_number(static_cast<uint32_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())\n                    {\n                        // uint 64\n                        oa->write_character(to_char_type(0xCF));\n                        write_number(static_cast<uint64_t>(j.m_value.number_integer));\n                    }\n                }\n                else\n                {\n                    if (j.m_value.number_integer >= -32)\n                    {\n                        // negative fixnum\n                        write_number(static_cast<int8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<int8_t>::min)() and\n                             j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())\n                    {\n                        // int 8\n                        oa->write_character(to_char_type(0xD0));\n                        write_number(static_cast<int8_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<int16_t>::min)() and\n                             j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())\n                    {\n                        // int 16\n                        oa->write_character(to_char_type(0xD1));\n                        write_number(static_cast<int16_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<int32_t>::min)() and\n                             j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())\n                    {\n                        // int 32\n                        oa->write_character(to_char_type(0xD2));\n                        write_number(static_cast<int32_t>(j.m_value.number_integer));\n                    }\n                    else if (j.m_value.number_integer >= (std::numeric_limits<int64_t>::min)() and\n                             j.m_value.number_integer <= (std::numeric_limits<int64_t>::max)())\n                    {\n                        // int 64\n                        oa->write_character(to_char_type(0xD3));\n                        write_number(static_cast<int64_t>(j.m_value.number_integer));\n                    }\n                }\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned < 128)\n                {\n                    // positive fixnum\n                    write_number(static_cast<uint8_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())\n                {\n                    // uint 8\n                    oa->write_character(to_char_type(0xCC));\n                    write_number(static_cast<uint8_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<uint16_t>::max)())\n                {\n                    // uint 16\n                    oa->write_character(to_char_type(0xCD));\n                    write_number(static_cast<uint16_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<uint32_t>::max)())\n                {\n                    // uint 32\n                    oa->write_character(to_char_type(0xCE));\n                    write_number(static_cast<uint32_t>(j.m_value.number_integer));\n                }\n                else if (j.m_value.number_unsigned <= (std::numeric_limits<uint64_t>::max)())\n                {\n                    // uint 64\n                    oa->write_character(to_char_type(0xCF));\n                    write_number(static_cast<uint64_t>(j.m_value.number_integer));\n                }\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                oa->write_character(get_msgpack_float_prefix(j.m_value.number_float));\n                write_number(j.m_value.number_float);\n                break;\n            }\n\n            case value_t::string:\n            {\n                // step 1: write control byte and the string length\n                const auto N = j.m_value.string->size();\n                if (N <= 31)\n                {\n                    // fixstr\n                    write_number(static_cast<uint8_t>(0xA0 | N));\n                }\n                else if (N <= (std::numeric_limits<uint8_t>::max)())\n                {\n                    // str 8\n                    oa->write_character(to_char_type(0xD9));\n                    write_number(static_cast<uint8_t>(N));\n                }\n                else if (N <= (std::numeric_limits<uint16_t>::max)())\n                {\n                    // str 16\n                    oa->write_character(to_char_type(0xDA));\n                    write_number(static_cast<uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<uint32_t>::max)())\n                {\n                    // str 32\n                    oa->write_character(to_char_type(0xDB));\n                    write_number(static_cast<uint32_t>(N));\n                }\n\n                // step 2: write the string\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                // step 1: write control byte and the array size\n                const auto N = j.m_value.array->size();\n                if (N <= 15)\n                {\n                    // fixarray\n                    write_number(static_cast<uint8_t>(0x90 | N));\n                }\n                else if (N <= (std::numeric_limits<uint16_t>::max)())\n                {\n                    // array 16\n                    oa->write_character(to_char_type(0xDC));\n                    write_number(static_cast<uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<uint32_t>::max)())\n                {\n                    // array 32\n                    oa->write_character(to_char_type(0xDD));\n                    write_number(static_cast<uint32_t>(N));\n                }\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_msgpack(el);\n                }\n                break;\n            }\n\n            case value_t::object:\n            {\n                // step 1: write control byte and the object size\n                const auto N = j.m_value.object->size();\n                if (N <= 15)\n                {\n                    // fixmap\n                    write_number(static_cast<uint8_t>(0x80 | (N & 0xF)));\n                }\n                else if (N <= (std::numeric_limits<uint16_t>::max)())\n                {\n                    // map 16\n                    oa->write_character(to_char_type(0xDE));\n                    write_number(static_cast<uint16_t>(N));\n                }\n                else if (N <= (std::numeric_limits<uint32_t>::max)())\n                {\n                    // map 32\n                    oa->write_character(to_char_type(0xDF));\n                    write_number(static_cast<uint32_t>(N));\n                }\n\n                // step 2: write each element\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_msgpack(el.first);\n                    write_msgpack(el.second);\n                }\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @param[in] use_count   whether to use '#' prefixes (optimized format)\n    @param[in] use_type    whether to use '$' prefixes (optimized format)\n    @param[in] add_prefix  whether prefixes need to be used for this value\n    */\n    void write_ubjson(const BasicJsonType& j, const bool use_count,\n                      const bool use_type, const bool add_prefix = true)\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('Z'));\n                }\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(j.m_value.boolean\n                                        ? to_char_type('T')\n                                        : to_char_type('F'));\n                }\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix);\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix);\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix);\n                break;\n            }\n\n            case value_t::string:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('S'));\n                }\n                write_number_with_ubjson_prefix(j.m_value.string->size(), true);\n                oa->write_characters(\n                    reinterpret_cast<const CharType*>(j.m_value.string->c_str()),\n                    j.m_value.string->size());\n                break;\n            }\n\n            case value_t::array:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('['));\n                }\n\n                bool prefix_required = true;\n                if (use_type and not j.m_value.array->empty())\n                {\n                    assert(use_count);\n                    const CharType first_prefix = ubjson_prefix(j.front());\n                    const bool same_prefix = std::all_of(j.begin() + 1, j.end(),\n                                                         [this, first_prefix](const BasicJsonType & v)\n                    {\n                        return ubjson_prefix(v) == first_prefix;\n                    });\n\n                    if (same_prefix)\n                    {\n                        prefix_required = false;\n                        oa->write_character(to_char_type('$'));\n                        oa->write_character(first_prefix);\n                    }\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.array->size(), true);\n                }\n\n                for (const auto& el : *j.m_value.array)\n                {\n                    write_ubjson(el, use_count, use_type, prefix_required);\n                }\n\n                if (not use_count)\n                {\n                    oa->write_character(to_char_type(']'));\n                }\n\n                break;\n            }\n\n            case value_t::object:\n            {\n                if (add_prefix)\n                {\n                    oa->write_character(to_char_type('{'));\n                }\n\n                bool prefix_required = true;\n                if (use_type and not j.m_value.object->empty())\n                {\n                    assert(use_count);\n                    const CharType first_prefix = ubjson_prefix(j.front());\n                    const bool same_prefix = std::all_of(j.begin(), j.end(),\n                                                         [this, first_prefix](const BasicJsonType & v)\n                    {\n                        return ubjson_prefix(v) == first_prefix;\n                    });\n\n                    if (same_prefix)\n                    {\n                        prefix_required = false;\n                        oa->write_character(to_char_type('$'));\n                        oa->write_character(first_prefix);\n                    }\n                }\n\n                if (use_count)\n                {\n                    oa->write_character(to_char_type('#'));\n                    write_number_with_ubjson_prefix(j.m_value.object->size(), true);\n                }\n\n                for (const auto& el : *j.m_value.object)\n                {\n                    write_number_with_ubjson_prefix(el.first.size(), true);\n                    oa->write_characters(\n                        reinterpret_cast<const CharType*>(el.first.c_str()),\n                        el.first.size());\n                    write_ubjson(el.second, use_count, use_type, prefix_required);\n                }\n\n                if (not use_count)\n                {\n                    oa->write_character(to_char_type('}'));\n                }\n\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n  private:\n    //////////\n    // BSON //\n    //////////\n\n    /*!\n    @return The size of a BSON document entry header, including the id marker\n            and the entry name size (and its null-terminator).\n    */\n    static std::size_t calc_bson_entry_header_size(const string_t& name)\n    {\n        const auto it = name.find(static_cast<typename string_t::value_type>(0));\n        if (JSON_UNLIKELY(it != BasicJsonType::string_t::npos))\n        {\n            JSON_THROW(out_of_range::create(409,\n                                            \"BSON key cannot contain code point U+0000 (at byte \" + std::to_string(it) + \")\"));\n        }\n\n        return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;\n    }\n\n    /*!\n    @brief Writes the given @a element_type and @a name to the output adapter\n    */\n    void write_bson_entry_header(const string_t& name,\n                                 const std::uint8_t element_type)\n    {\n        oa->write_character(to_char_type(element_type)); // boolean\n        oa->write_characters(\n            reinterpret_cast<const CharType*>(name.c_str()),\n            name.size() + 1u);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and boolean value @a value\n    */\n    void write_bson_boolean(const string_t& name,\n                            const bool value)\n    {\n        write_bson_entry_header(name, 0x08);\n        oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00));\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and double value @a value\n    */\n    void write_bson_double(const string_t& name,\n                           const double value)\n    {\n        write_bson_entry_header(name, 0x01);\n        write_number<double, true>(value);\n    }\n\n    /*!\n    @return The size of the BSON-encoded string in @a value\n    */\n    static std::size_t calc_bson_string_size(const string_t& value)\n    {\n        return sizeof(std::int32_t) + value.size() + 1ul;\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and string value @a value\n    */\n    void write_bson_string(const string_t& name,\n                           const string_t& value)\n    {\n        write_bson_entry_header(name, 0x02);\n\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(value.size() + 1ul));\n        oa->write_characters(\n            reinterpret_cast<const CharType*>(value.c_str()),\n            value.size() + 1);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and null value\n    */\n    void write_bson_null(const string_t& name)\n    {\n        write_bson_entry_header(name, 0x0A);\n    }\n\n    /*!\n    @return The size of the BSON-encoded integer @a value\n    */\n    static std::size_t calc_bson_integer_size(const std::int64_t value)\n    {\n        if ((std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)())\n        {\n            return sizeof(std::int32_t);\n        }\n        else\n        {\n            return sizeof(std::int64_t);\n        }\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and integer @a value\n    */\n    void write_bson_integer(const string_t& name,\n                            const std::int64_t value)\n    {\n        if ((std::numeric_limits<std::int32_t>::min)() <= value and value <= (std::numeric_limits<std::int32_t>::max)())\n        {\n            write_bson_entry_header(name, 0x10); // int32\n            write_number<std::int32_t, true>(static_cast<std::int32_t>(value));\n        }\n        else\n        {\n            write_bson_entry_header(name, 0x12); // int64\n            write_number<std::int64_t, true>(static_cast<std::int64_t>(value));\n        }\n    }\n\n    /*!\n    @return The size of the BSON-encoded unsigned integer in @a j\n    */\n    static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept\n    {\n        return (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n               ? sizeof(std::int32_t)\n               : sizeof(std::int64_t);\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and unsigned @a value\n    */\n    void write_bson_unsigned(const string_t& name,\n                             const std::uint64_t value)\n    {\n        if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))\n        {\n            write_bson_entry_header(name, 0x10 /* int32 */);\n            write_number<std::int32_t, true>(static_cast<std::int32_t>(value));\n        }\n        else if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))\n        {\n            write_bson_entry_header(name, 0x12 /* int64 */);\n            write_number<std::int64_t, true>(static_cast<std::int64_t>(value));\n        }\n        else\n        {\n            JSON_THROW(out_of_range::create(407, \"integer number \" + std::to_string(value) + \" cannot be represented by BSON as it does not fit int64\"));\n        }\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and object @a value\n    */\n    void write_bson_object_entry(const string_t& name,\n                                 const typename BasicJsonType::object_t& value)\n    {\n        write_bson_entry_header(name, 0x03); // object\n        write_bson_object(value);\n    }\n\n    /*!\n    @return The size of the BSON-encoded array @a value\n    */\n    static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value)\n    {\n        std::size_t embedded_document_size = 0ul;\n        std::size_t array_index = 0ul;\n\n        for (const auto& el : value)\n        {\n            embedded_document_size += calc_bson_element_size(std::to_string(array_index++), el);\n        }\n\n        return sizeof(std::int32_t) + embedded_document_size + 1ul;\n    }\n\n    /*!\n    @brief Writes a BSON element with key @a name and array @a value\n    */\n    void write_bson_array(const string_t& name,\n                          const typename BasicJsonType::array_t& value)\n    {\n        write_bson_entry_header(name, 0x04); // array\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_array_size(value)));\n\n        std::size_t array_index = 0ul;\n\n        for (const auto& el : value)\n        {\n            write_bson_element(std::to_string(array_index++), el);\n        }\n\n        oa->write_character(to_char_type(0x00));\n    }\n\n    /*!\n    @brief Calculates the size necessary to serialize the JSON value @a j with its @a name\n    @return The calculated size for the BSON document entry for @a j with the given @a name.\n    */\n    static std::size_t calc_bson_element_size(const string_t& name,\n            const BasicJsonType& j)\n    {\n        const auto header_size = calc_bson_entry_header_size(name);\n        switch (j.type())\n        {\n            case value_t::object:\n                return header_size + calc_bson_object_size(*j.m_value.object);\n\n            case value_t::array:\n                return header_size + calc_bson_array_size(*j.m_value.array);\n\n            case value_t::boolean:\n                return header_size + 1ul;\n\n            case value_t::number_float:\n                return header_size + 8ul;\n\n            case value_t::number_integer:\n                return header_size + calc_bson_integer_size(j.m_value.number_integer);\n\n            case value_t::number_unsigned:\n                return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned);\n\n            case value_t::string:\n                return header_size + calc_bson_string_size(*j.m_value.string);\n\n            case value_t::null:\n                return header_size + 0ul;\n\n            // LCOV_EXCL_START\n            default:\n                assert(false);\n                return 0ul;\n                // LCOV_EXCL_STOP\n        };\n    }\n\n    /*!\n    @brief Serializes the JSON value @a j to BSON and associates it with the\n           key @a name.\n    @param name The name to associate with the JSON entity @a j within the\n                current BSON document\n    @return The size of the BSON entry\n    */\n    void write_bson_element(const string_t& name,\n                            const BasicJsonType& j)\n    {\n        switch (j.type())\n        {\n            case value_t::object:\n                return write_bson_object_entry(name, *j.m_value.object);\n\n            case value_t::array:\n                return write_bson_array(name, *j.m_value.array);\n\n            case value_t::boolean:\n                return write_bson_boolean(name, j.m_value.boolean);\n\n            case value_t::number_float:\n                return write_bson_double(name, j.m_value.number_float);\n\n            case value_t::number_integer:\n                return write_bson_integer(name, j.m_value.number_integer);\n\n            case value_t::number_unsigned:\n                return write_bson_unsigned(name, j.m_value.number_unsigned);\n\n            case value_t::string:\n                return write_bson_string(name, *j.m_value.string);\n\n            case value_t::null:\n                return write_bson_null(name);\n\n            // LCOV_EXCL_START\n            default:\n                assert(false);\n                return;\n                // LCOV_EXCL_STOP\n        };\n    }\n\n    /*!\n    @brief Calculates the size of the BSON serialization of the given\n           JSON-object @a j.\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value)\n    {\n        std::size_t document_size = std::accumulate(value.begin(), value.end(), 0ul,\n                                    [](size_t result, const typename BasicJsonType::object_t::value_type & el)\n        {\n            return result += calc_bson_element_size(el.first, el.second);\n        });\n\n        return sizeof(std::int32_t) + document_size + 1ul;\n    }\n\n    /*!\n    @param[in] j  JSON value to serialize\n    @pre       j.type() == value_t::object\n    */\n    void write_bson_object(const typename BasicJsonType::object_t& value)\n    {\n        write_number<std::int32_t, true>(static_cast<std::int32_t>(calc_bson_object_size(value)));\n\n        for (const auto& el : value)\n        {\n            write_bson_element(el.first, el.second);\n        }\n\n        oa->write_character(to_char_type(0x00));\n    }\n\n    //////////\n    // CBOR //\n    //////////\n\n    static constexpr CharType get_cbor_float_prefix(float /*unused*/)\n    {\n        return to_char_type(0xFA);  // Single-Precision Float\n    }\n\n    static constexpr CharType get_cbor_float_prefix(double /*unused*/)\n    {\n        return to_char_type(0xFB);  // Double-Precision Float\n    }\n\n    /////////////\n    // MsgPack //\n    /////////////\n\n    static constexpr CharType get_msgpack_float_prefix(float /*unused*/)\n    {\n        return to_char_type(0xCA);  // float 32\n    }\n\n    static constexpr CharType get_msgpack_float_prefix(double /*unused*/)\n    {\n        return to_char_type(0xCB);  // float 64\n    }\n\n    ////////////\n    // UBJSON //\n    ////////////\n\n    // UBJSON: write number (floating point)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_floating_point<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if (add_prefix)\n        {\n            oa->write_character(get_ubjson_float_prefix(n));\n        }\n        write_number(n);\n    }\n\n    // UBJSON: write number (unsigned integer)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_unsigned<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if (n <= static_cast<uint64_t>((std::numeric_limits<int8_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('i'));  // int8\n            }\n            write_number(static_cast<uint8_t>(n));\n        }\n        else if (n <= (std::numeric_limits<uint8_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('U'));  // uint8\n            }\n            write_number(static_cast<uint8_t>(n));\n        }\n        else if (n <= static_cast<uint64_t>((std::numeric_limits<int16_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('I'));  // int16\n            }\n            write_number(static_cast<int16_t>(n));\n        }\n        else if (n <= static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('l'));  // int32\n            }\n            write_number(static_cast<int32_t>(n));\n        }\n        else if (n <= static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('L'));  // int64\n            }\n            write_number(static_cast<int64_t>(n));\n        }\n        else\n        {\n            JSON_THROW(out_of_range::create(407, \"integer number \" + std::to_string(n) + \" cannot be represented by UBJSON as it does not fit int64\"));\n        }\n    }\n\n    // UBJSON: write number (signed integer)\n    template<typename NumberType, typename std::enable_if<\n                 std::is_signed<NumberType>::value and\n                 not std::is_floating_point<NumberType>::value, int>::type = 0>\n    void write_number_with_ubjson_prefix(const NumberType n,\n                                         const bool add_prefix)\n    {\n        if ((std::numeric_limits<int8_t>::min)() <= n and n <= (std::numeric_limits<int8_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('i'));  // int8\n            }\n            write_number(static_cast<int8_t>(n));\n        }\n        else if (static_cast<int64_t>((std::numeric_limits<uint8_t>::min)()) <= n and n <= static_cast<int64_t>((std::numeric_limits<uint8_t>::max)()))\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('U'));  // uint8\n            }\n            write_number(static_cast<uint8_t>(n));\n        }\n        else if ((std::numeric_limits<int16_t>::min)() <= n and n <= (std::numeric_limits<int16_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('I'));  // int16\n            }\n            write_number(static_cast<int16_t>(n));\n        }\n        else if ((std::numeric_limits<int32_t>::min)() <= n and n <= (std::numeric_limits<int32_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('l'));  // int32\n            }\n            write_number(static_cast<int32_t>(n));\n        }\n        else if ((std::numeric_limits<int64_t>::min)() <= n and n <= (std::numeric_limits<int64_t>::max)())\n        {\n            if (add_prefix)\n            {\n                oa->write_character(to_char_type('L'));  // int64\n            }\n            write_number(static_cast<int64_t>(n));\n        }\n        // LCOV_EXCL_START\n        else\n        {\n            JSON_THROW(out_of_range::create(407, \"integer number \" + std::to_string(n) + \" cannot be represented by UBJSON as it does not fit int64\"));\n        }\n        // LCOV_EXCL_STOP\n    }\n\n    /*!\n    @brief determine the type prefix of container values\n\n    @note This function does not need to be 100% accurate when it comes to\n          integer limits. In case a number exceeds the limits of int64_t,\n          this will be detected by a later call to function\n          write_number_with_ubjson_prefix. Therefore, we return 'L' for any\n          value that does not fit the previous limits.\n    */\n    CharType ubjson_prefix(const BasicJsonType& j) const noexcept\n    {\n        switch (j.type())\n        {\n            case value_t::null:\n                return 'Z';\n\n            case value_t::boolean:\n                return j.m_value.boolean ? 'T' : 'F';\n\n            case value_t::number_integer:\n            {\n                if ((std::numeric_limits<int8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int8_t>::max)())\n                {\n                    return 'i';\n                }\n                if ((std::numeric_limits<uint8_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<uint8_t>::max)())\n                {\n                    return 'U';\n                }\n                if ((std::numeric_limits<int16_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int16_t>::max)())\n                {\n                    return 'I';\n                }\n                if ((std::numeric_limits<int32_t>::min)() <= j.m_value.number_integer and j.m_value.number_integer <= (std::numeric_limits<int32_t>::max)())\n                {\n                    return 'l';\n                }\n                // no check and assume int64_t (see note above)\n                return 'L';\n            }\n\n            case value_t::number_unsigned:\n            {\n                if (j.m_value.number_unsigned <= (std::numeric_limits<int8_t>::max)())\n                {\n                    return 'i';\n                }\n                if (j.m_value.number_unsigned <= (std::numeric_limits<uint8_t>::max)())\n                {\n                    return 'U';\n                }\n                if (j.m_value.number_unsigned <= (std::numeric_limits<int16_t>::max)())\n                {\n                    return 'I';\n                }\n                if (j.m_value.number_unsigned <= (std::numeric_limits<int32_t>::max)())\n                {\n                    return 'l';\n                }\n                // no check and assume int64_t (see note above)\n                return 'L';\n            }\n\n            case value_t::number_float:\n                return get_ubjson_float_prefix(j.m_value.number_float);\n\n            case value_t::string:\n                return 'S';\n\n            case value_t::array:\n                return '[';\n\n            case value_t::object:\n                return '{';\n\n            default:  // discarded values\n                return 'N';\n        }\n    }\n\n    static constexpr CharType get_ubjson_float_prefix(float /*unused*/)\n    {\n        return 'd';  // float 32\n    }\n\n    static constexpr CharType get_ubjson_float_prefix(double /*unused*/)\n    {\n        return 'D';  // float 64\n    }\n\n    ///////////////////////\n    // Utility functions //\n    ///////////////////////\n\n    /*\n    @brief write a number to output input\n    @param[in] n number of type @a NumberType\n    @tparam NumberType the type of the number\n    @tparam OutputIsLittleEndian Set to true if output data is\n                                 required to be little endian\n\n    @note This function needs to respect the system's endianess, because bytes\n          in CBOR, MessagePack, and UBJSON are stored in network order (big\n          endian) and therefore need reordering on little endian systems.\n    */\n    template<typename NumberType, bool OutputIsLittleEndian = false>\n    void write_number(const NumberType n)\n    {\n        // step 1: write number to array of length NumberType\n        std::array<CharType, sizeof(NumberType)> vec;\n        std::memcpy(vec.data(), &n, sizeof(NumberType));\n\n        // step 2: write array to output (with possible reordering)\n        if (is_little_endian and not OutputIsLittleEndian)\n        {\n            // reverse byte order prior to conversion if necessary\n            std::reverse(vec.begin(), vec.end());\n        }\n\n        oa->write_characters(vec.data(), sizeof(NumberType));\n    }\n\n  public:\n    // The following to_char_type functions are implement the conversion\n    // between uint8_t and CharType. In case CharType is not unsigned,\n    // such a conversion is required to allow values greater than 128.\n    // See <https://github.com/nlohmann/json/issues/1286> for a discussion.\n    template < typename C = CharType,\n               enable_if_t < std::is_signed<C>::value and std::is_signed<char>::value > * = nullptr >\n    static constexpr CharType to_char_type(std::uint8_t x) noexcept\n    {\n        return *reinterpret_cast<char*>(&x);\n    }\n\n    template < typename C = CharType,\n               enable_if_t < std::is_signed<C>::value and std::is_unsigned<char>::value > * = nullptr >\n    static CharType to_char_type(std::uint8_t x) noexcept\n    {\n        static_assert(sizeof(std::uint8_t) == sizeof(CharType), \"size of CharType must be equal to std::uint8_t\");\n        static_assert(std::is_pod<CharType>::value, \"CharType must be POD\");\n        CharType result;\n        std::memcpy(&result, &x, sizeof(x));\n        return result;\n    }\n\n    template<typename C = CharType,\n             enable_if_t<std::is_unsigned<C>::value>* = nullptr>\n    static constexpr CharType to_char_type(std::uint8_t x) noexcept\n    {\n        return x;\n    }\n\n    template < typename InputCharType, typename C = CharType,\n               enable_if_t <\n                   std::is_signed<C>::value and\n                   std::is_signed<char>::value and\n                   std::is_same<char, typename std::remove_cv<InputCharType>::type>::value\n                   > * = nullptr >\n    static constexpr CharType to_char_type(InputCharType x) noexcept\n    {\n        return x;\n    }\n\n  private:\n    /// whether we can assume little endianess\n    const bool is_little_endian = binary_reader<BasicJsonType>::little_endianess();\n\n    /// the output\n    output_adapter_t<CharType> oa = nullptr;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/output/serializer.hpp>\n\n\n#include <algorithm> // reverse, remove, fill, find, none_of\n#include <array> // array\n#include <cassert> // assert\n#include <ciso646> // and, or\n#include <clocale> // localeconv, lconv\n#include <cmath> // labs, isfinite, isnan, signbit\n#include <cstddef> // size_t, ptrdiff_t\n#include <cstdint> // uint8_t\n#include <cstdio> // snprintf\n#include <limits> // numeric_limits\n#include <string> // string\n#include <type_traits> // is_same\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/conversions/to_chars.hpp>\n\n\n#include <cassert> // assert\n#include <ciso646> // or, and, not\n#include <cmath>   // signbit, isfinite\n#include <cstdint> // intN_t, uintN_t\n#include <cstring> // memcpy, memmove\n\nnamespace nlohmann\n{\nnamespace detail\n{\n\n/*!\n@brief implements the Grisu2 algorithm for binary to decimal floating-point\nconversion.\n\nThis implementation is a slightly modified version of the reference\nimplementation which may be obtained from\nhttp://florian.loitsch.com/publications (bench.tar.gz).\n\nThe code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch.\n\nFor a detailed description of the algorithm see:\n\n[1] Loitsch, \"Printing Floating-Point Numbers Quickly and Accurately with\n    Integers\", Proceedings of the ACM SIGPLAN 2010 Conference on Programming\n    Language Design and Implementation, PLDI 2010\n[2] Burger, Dybvig, \"Printing Floating-Point Numbers Quickly and Accurately\",\n    Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language\n    Design and Implementation, PLDI 1996\n*/\nnamespace dtoa_impl\n{\n\ntemplate <typename Target, typename Source>\nTarget reinterpret_bits(const Source source)\n{\n    static_assert(sizeof(Target) == sizeof(Source), \"size mismatch\");\n\n    Target target;\n    std::memcpy(&target, &source, sizeof(Source));\n    return target;\n}\n\nstruct diyfp // f * 2^e\n{\n    static constexpr int kPrecision = 64; // = q\n\n    uint64_t f = 0;\n    int e = 0;\n\n    constexpr diyfp(uint64_t f_, int e_) noexcept : f(f_), e(e_) {}\n\n    /*!\n    @brief returns x - y\n    @pre x.e == y.e and x.f >= y.f\n    */\n    static diyfp sub(const diyfp& x, const diyfp& y) noexcept\n    {\n        assert(x.e == y.e);\n        assert(x.f >= y.f);\n\n        return {x.f - y.f, x.e};\n    }\n\n    /*!\n    @brief returns x * y\n    @note The result is rounded. (Only the upper q bits are returned.)\n    */\n    static diyfp mul(const diyfp& x, const diyfp& y) noexcept\n    {\n        static_assert(kPrecision == 64, \"internal error\");\n\n        // Computes:\n        //  f = round((x.f * y.f) / 2^q)\n        //  e = x.e + y.e + q\n\n        // Emulate the 64-bit * 64-bit multiplication:\n        //\n        // p = u * v\n        //   = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)\n        //   = (u_lo v_lo         ) + 2^32 ((u_lo v_hi         ) + (u_hi v_lo         )) + 2^64 (u_hi v_hi         )\n        //   = (p0                ) + 2^32 ((p1                ) + (p2                )) + 2^64 (p3                )\n        //   = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3                )\n        //   = (p0_lo             ) + 2^32 (p0_hi + p1_lo + p2_lo                      ) + 2^64 (p1_hi + p2_hi + p3)\n        //   = (p0_lo             ) + 2^32 (Q                                          ) + 2^64 (H                 )\n        //   = (p0_lo             ) + 2^32 (Q_lo + 2^32 Q_hi                           ) + 2^64 (H                 )\n        //\n        // (Since Q might be larger than 2^32 - 1)\n        //\n        //   = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)\n        //\n        // (Q_hi + H does not overflow a 64-bit int)\n        //\n        //   = p_lo + 2^64 p_hi\n\n        const uint64_t u_lo = x.f & 0xFFFFFFFF;\n        const uint64_t u_hi = x.f >> 32;\n        const uint64_t v_lo = y.f & 0xFFFFFFFF;\n        const uint64_t v_hi = y.f >> 32;\n\n        const uint64_t p0 = u_lo * v_lo;\n        const uint64_t p1 = u_lo * v_hi;\n        const uint64_t p2 = u_hi * v_lo;\n        const uint64_t p3 = u_hi * v_hi;\n\n        const uint64_t p0_hi = p0 >> 32;\n        const uint64_t p1_lo = p1 & 0xFFFFFFFF;\n        const uint64_t p1_hi = p1 >> 32;\n        const uint64_t p2_lo = p2 & 0xFFFFFFFF;\n        const uint64_t p2_hi = p2 >> 32;\n\n        uint64_t Q = p0_hi + p1_lo + p2_lo;\n\n        // The full product might now be computed as\n        //\n        // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)\n        // p_lo = p0_lo + (Q << 32)\n        //\n        // But in this particular case here, the full p_lo is not required.\n        // Effectively we only need to add the highest bit in p_lo to p_hi (and\n        // Q_hi + 1 does not overflow).\n\n        Q += uint64_t{1} << (64 - 32 - 1); // round, ties up\n\n        const uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32);\n\n        return {h, x.e + y.e + 64};\n    }\n\n    /*!\n    @brief normalize x such that the significand is >= 2^(q-1)\n    @pre x.f != 0\n    */\n    static diyfp normalize(diyfp x) noexcept\n    {\n        assert(x.f != 0);\n\n        while ((x.f >> 63) == 0)\n        {\n            x.f <<= 1;\n            x.e--;\n        }\n\n        return x;\n    }\n\n    /*!\n    @brief normalize x such that the result has the exponent E\n    @pre e >= x.e and the upper e - x.e bits of x.f must be zero.\n    */\n    static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept\n    {\n        const int delta = x.e - target_exponent;\n\n        assert(delta >= 0);\n        assert(((x.f << delta) >> delta) == x.f);\n\n        return {x.f << delta, target_exponent};\n    }\n};\n\nstruct boundaries\n{\n    diyfp w;\n    diyfp minus;\n    diyfp plus;\n};\n\n/*!\nCompute the (normalized) diyfp representing the input number 'value' and its\nboundaries.\n\n@pre value must be finite and positive\n*/\ntemplate <typename FloatType>\nboundaries compute_boundaries(FloatType value)\n{\n    assert(std::isfinite(value));\n    assert(value > 0);\n\n    // Convert the IEEE representation into a diyfp.\n    //\n    // If v is denormal:\n    //      value = 0.F * 2^(1 - bias) = (          F) * 2^(1 - bias - (p-1))\n    // If v is normalized:\n    //      value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))\n\n    static_assert(std::numeric_limits<FloatType>::is_iec559,\n                  \"internal error: dtoa_short requires an IEEE-754 floating-point implementation\");\n\n    constexpr int      kPrecision = std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)\n    constexpr int      kBias      = std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);\n    constexpr int      kMinExp    = 1 - kBias;\n    constexpr uint64_t kHiddenBit = uint64_t{1} << (kPrecision - 1); // = 2^(p-1)\n\n    using bits_type = typename std::conditional< kPrecision == 24, uint32_t, uint64_t >::type;\n\n    const uint64_t bits = reinterpret_bits<bits_type>(value);\n    const uint64_t E = bits >> (kPrecision - 1);\n    const uint64_t F = bits & (kHiddenBit - 1);\n\n    const bool is_denormal = (E == 0);\n    const diyfp v = is_denormal\n                    ? diyfp(F, kMinExp)\n                    : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);\n\n    // Compute the boundaries m- and m+ of the floating-point value\n    // v = f * 2^e.\n    //\n    // Determine v- and v+, the floating-point predecessor and successor if v,\n    // respectively.\n    //\n    //      v- = v - 2^e        if f != 2^(p-1) or e == e_min                (A)\n    //         = v - 2^(e-1)    if f == 2^(p-1) and e > e_min                (B)\n    //\n    //      v+ = v + 2^e\n    //\n    // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_\n    // between m- and m+ round to v, regardless of how the input rounding\n    // algorithm breaks ties.\n    //\n    //      ---+-------------+-------------+-------------+-------------+---  (A)\n    //         v-            m-            v             m+            v+\n    //\n    //      -----------------+------+------+-------------+-------------+---  (B)\n    //                       v-     m-     v             m+            v+\n\n    const bool lower_boundary_is_closer = (F == 0 and E > 1);\n    const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);\n    const diyfp m_minus = lower_boundary_is_closer\n                          ? diyfp(4 * v.f - 1, v.e - 2)  // (B)\n                          : diyfp(2 * v.f - 1, v.e - 1); // (A)\n\n    // Determine the normalized w+ = m+.\n    const diyfp w_plus = diyfp::normalize(m_plus);\n\n    // Determine w- = m- such that e_(w-) = e_(w+).\n    const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);\n\n    return {diyfp::normalize(v), w_minus, w_plus};\n}\n\n// Given normalized diyfp w, Grisu needs to find a (normalized) cached\n// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies\n// within a certain range [alpha, gamma] (Definition 3.2 from [1])\n//\n//      alpha <= e = e_c + e_w + q <= gamma\n//\n// or\n//\n//      f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q\n//                          <= f_c * f_w * 2^gamma\n//\n// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies\n//\n//      2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma\n//\n// or\n//\n//      2^(q - 2 + alpha) <= c * w < 2^(q + gamma)\n//\n// The choice of (alpha,gamma) determines the size of the table and the form of\n// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well\n// in practice:\n//\n// The idea is to cut the number c * w = f * 2^e into two parts, which can be\n// processed independently: An integral part p1, and a fractional part p2:\n//\n//      f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e\n//              = (f div 2^-e) + (f mod 2^-e) * 2^e\n//              = p1 + p2 * 2^e\n//\n// The conversion of p1 into decimal form requires a series of divisions and\n// modulos by (a power of) 10. These operations are faster for 32-bit than for\n// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be\n// achieved by choosing\n//\n//      -e >= 32   or   e <= -32 := gamma\n//\n// In order to convert the fractional part\n//\n//      p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...\n//\n// into decimal form, the fraction is repeatedly multiplied by 10 and the digits\n// d[-i] are extracted in order:\n//\n//      (10 * p2) div 2^-e = d[-1]\n//      (10 * p2) mod 2^-e = d[-2] / 10^1 + ...\n//\n// The multiplication by 10 must not overflow. It is sufficient to choose\n//\n//      10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.\n//\n// Since p2 = f mod 2^-e < 2^-e,\n//\n//      -e <= 60   or   e >= -60 := alpha\n\nconstexpr int kAlpha = -60;\nconstexpr int kGamma = -32;\n\nstruct cached_power // c = f * 2^e ~= 10^k\n{\n    uint64_t f;\n    int e;\n    int k;\n};\n\n/*!\nFor a normalized diyfp w = f * 2^e, this function returns a (normalized) cached\npower-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c\nsatisfies (Definition 3.2 from [1])\n\n     alpha <= e_c + e + q <= gamma.\n*/\ninline cached_power get_cached_power_for_binary_exponent(int e)\n{\n    // Now\n    //\n    //      alpha <= e_c + e + q <= gamma                                    (1)\n    //      ==> f_c * 2^alpha <= c * 2^e * 2^q\n    //\n    // and since the c's are normalized, 2^(q-1) <= f_c,\n    //\n    //      ==> 2^(q - 1 + alpha) <= c * 2^(e + q)\n    //      ==> 2^(alpha - e - 1) <= c\n    //\n    // If c were an exakt power of ten, i.e. c = 10^k, one may determine k as\n    //\n    //      k = ceil( log_10( 2^(alpha - e - 1) ) )\n    //        = ceil( (alpha - e - 1) * log_10(2) )\n    //\n    // From the paper:\n    // \"In theory the result of the procedure could be wrong since c is rounded,\n    //  and the computation itself is approximated [...]. In practice, however,\n    //  this simple function is sufficient.\"\n    //\n    // For IEEE double precision floating-point numbers converted into\n    // normalized diyfp's w = f * 2^e, with q = 64,\n    //\n    //      e >= -1022      (min IEEE exponent)\n    //           -52        (p - 1)\n    //           -52        (p - 1, possibly normalize denormal IEEE numbers)\n    //           -11        (normalize the diyfp)\n    //         = -1137\n    //\n    // and\n    //\n    //      e <= +1023      (max IEEE exponent)\n    //           -52        (p - 1)\n    //           -11        (normalize the diyfp)\n    //         = 960\n    //\n    // This binary exponent range [-1137,960] results in a decimal exponent\n    // range [-307,324]. One does not need to store a cached power for each\n    // k in this range. For each such k it suffices to find a cached power\n    // such that the exponent of the product lies in [alpha,gamma].\n    // This implies that the difference of the decimal exponents of adjacent\n    // table entries must be less than or equal to\n    //\n    //      floor( (gamma - alpha) * log_10(2) ) = 8.\n    //\n    // (A smaller distance gamma-alpha would require a larger table.)\n\n    // NB:\n    // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.\n\n    constexpr int kCachedPowersSize = 79;\n    constexpr int kCachedPowersMinDecExp = -300;\n    constexpr int kCachedPowersDecStep = 8;\n\n    static constexpr cached_power kCachedPowers[] =\n    {\n        { 0xAB70FE17C79AC6CA, -1060, -300 },\n        { 0xFF77B1FCBEBCDC4F, -1034, -292 },\n        { 0xBE5691EF416BD60C, -1007, -284 },\n        { 0x8DD01FAD907FFC3C,  -980, -276 },\n        { 0xD3515C2831559A83,  -954, -268 },\n        { 0x9D71AC8FADA6C9B5,  -927, -260 },\n        { 0xEA9C227723EE8BCB,  -901, -252 },\n        { 0xAECC49914078536D,  -874, -244 },\n        { 0x823C12795DB6CE57,  -847, -236 },\n        { 0xC21094364DFB5637,  -821, -228 },\n        { 0x9096EA6F3848984F,  -794, -220 },\n        { 0xD77485CB25823AC7,  -768, -212 },\n        { 0xA086CFCD97BF97F4,  -741, -204 },\n        { 0xEF340A98172AACE5,  -715, -196 },\n        { 0xB23867FB2A35B28E,  -688, -188 },\n        { 0x84C8D4DFD2C63F3B,  -661, -180 },\n        { 0xC5DD44271AD3CDBA,  -635, -172 },\n        { 0x936B9FCEBB25C996,  -608, -164 },\n        { 0xDBAC6C247D62A584,  -582, -156 },\n        { 0xA3AB66580D5FDAF6,  -555, -148 },\n        { 0xF3E2F893DEC3F126,  -529, -140 },\n        { 0xB5B5ADA8AAFF80B8,  -502, -132 },\n        { 0x87625F056C7C4A8B,  -475, -124 },\n        { 0xC9BCFF6034C13053,  -449, -116 },\n        { 0x964E858C91BA2655,  -422, -108 },\n        { 0xDFF9772470297EBD,  -396, -100 },\n        { 0xA6DFBD9FB8E5B88F,  -369,  -92 },\n        { 0xF8A95FCF88747D94,  -343,  -84 },\n        { 0xB94470938FA89BCF,  -316,  -76 },\n        { 0x8A08F0F8BF0F156B,  -289,  -68 },\n        { 0xCDB02555653131B6,  -263,  -60 },\n        { 0x993FE2C6D07B7FAC,  -236,  -52 },\n        { 0xE45C10C42A2B3B06,  -210,  -44 },\n        { 0xAA242499697392D3,  -183,  -36 },\n        { 0xFD87B5F28300CA0E,  -157,  -28 },\n        { 0xBCE5086492111AEB,  -130,  -20 },\n        { 0x8CBCCC096F5088CC,  -103,  -12 },\n        { 0xD1B71758E219652C,   -77,   -4 },\n        { 0x9C40000000000000,   -50,    4 },\n        { 0xE8D4A51000000000,   -24,   12 },\n        { 0xAD78EBC5AC620000,     3,   20 },\n        { 0x813F3978F8940984,    30,   28 },\n        { 0xC097CE7BC90715B3,    56,   36 },\n        { 0x8F7E32CE7BEA5C70,    83,   44 },\n        { 0xD5D238A4ABE98068,   109,   52 },\n        { 0x9F4F2726179A2245,   136,   60 },\n        { 0xED63A231D4C4FB27,   162,   68 },\n        { 0xB0DE65388CC8ADA8,   189,   76 },\n        { 0x83C7088E1AAB65DB,   216,   84 },\n        { 0xC45D1DF942711D9A,   242,   92 },\n        { 0x924D692CA61BE758,   269,  100 },\n        { 0xDA01EE641A708DEA,   295,  108 },\n        { 0xA26DA3999AEF774A,   322,  116 },\n        { 0xF209787BB47D6B85,   348,  124 },\n        { 0xB454E4A179DD1877,   375,  132 },\n        { 0x865B86925B9BC5C2,   402,  140 },\n        { 0xC83553C5C8965D3D,   428,  148 },\n        { 0x952AB45CFA97A0B3,   455,  156 },\n        { 0xDE469FBD99A05FE3,   481,  164 },\n        { 0xA59BC234DB398C25,   508,  172 },\n        { 0xF6C69A72A3989F5C,   534,  180 },\n        { 0xB7DCBF5354E9BECE,   561,  188 },\n        { 0x88FCF317F22241E2,   588,  196 },\n        { 0xCC20CE9BD35C78A5,   614,  204 },\n        { 0x98165AF37B2153DF,   641,  212 },\n        { 0xE2A0B5DC971F303A,   667,  220 },\n        { 0xA8D9D1535CE3B396,   694,  228 },\n        { 0xFB9B7CD9A4A7443C,   720,  236 },\n        { 0xBB764C4CA7A44410,   747,  244 },\n        { 0x8BAB8EEFB6409C1A,   774,  252 },\n        { 0xD01FEF10A657842C,   800,  260 },\n        { 0x9B10A4E5E9913129,   827,  268 },\n        { 0xE7109BFBA19C0C9D,   853,  276 },\n        { 0xAC2820D9623BF429,   880,  284 },\n        { 0x80444B5E7AA7CF85,   907,  292 },\n        { 0xBF21E44003ACDD2D,   933,  300 },\n        { 0x8E679C2F5E44FF8F,   960,  308 },\n        { 0xD433179D9C8CB841,   986,  316 },\n        { 0x9E19DB92B4E31BA9,  1013,  324 },\n    };\n\n    // This computation gives exactly the same results for k as\n    //      k = ceil((kAlpha - e - 1) * 0.30102999566398114)\n    // for |e| <= 1500, but doesn't require floating-point operations.\n    // NB: log_10(2) ~= 78913 / 2^18\n    assert(e >= -1500);\n    assert(e <=  1500);\n    const int f = kAlpha - e - 1;\n    const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);\n\n    const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep;\n    assert(index >= 0);\n    assert(index < kCachedPowersSize);\n    static_cast<void>(kCachedPowersSize); // Fix warning.\n\n    const cached_power cached = kCachedPowers[index];\n    assert(kAlpha <= cached.e + e + 64);\n    assert(kGamma >= cached.e + e + 64);\n\n    return cached;\n}\n\n/*!\nFor n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.\nFor n == 0, returns 1 and sets pow10 := 1.\n*/\ninline int find_largest_pow10(const uint32_t n, uint32_t& pow10)\n{\n    // LCOV_EXCL_START\n    if (n >= 1000000000)\n    {\n        pow10 = 1000000000;\n        return 10;\n    }\n    // LCOV_EXCL_STOP\n    else if (n >= 100000000)\n    {\n        pow10 = 100000000;\n        return  9;\n    }\n    else if (n >= 10000000)\n    {\n        pow10 = 10000000;\n        return  8;\n    }\n    else if (n >= 1000000)\n    {\n        pow10 = 1000000;\n        return  7;\n    }\n    else if (n >= 100000)\n    {\n        pow10 = 100000;\n        return  6;\n    }\n    else if (n >= 10000)\n    {\n        pow10 = 10000;\n        return  5;\n    }\n    else if (n >= 1000)\n    {\n        pow10 = 1000;\n        return  4;\n    }\n    else if (n >= 100)\n    {\n        pow10 = 100;\n        return  3;\n    }\n    else if (n >= 10)\n    {\n        pow10 = 10;\n        return  2;\n    }\n    else\n    {\n        pow10 = 1;\n        return 1;\n    }\n}\n\ninline void grisu2_round(char* buf, int len, uint64_t dist, uint64_t delta,\n                         uint64_t rest, uint64_t ten_k)\n{\n    assert(len >= 1);\n    assert(dist <= delta);\n    assert(rest <= delta);\n    assert(ten_k > 0);\n\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    //                                  ten_k\n    //                                <------>\n    //                                       <---- rest ---->\n    // --------------[------------------+----+--------------]--------------\n    //                                  w    V\n    //                                       = buf * 10^k\n    //\n    // ten_k represents a unit-in-the-last-place in the decimal representation\n    // stored in buf.\n    // Decrement buf by ten_k while this takes buf closer to w.\n\n    // The tests are written in this order to avoid overflow in unsigned\n    // integer arithmetic.\n\n    while (rest < dist\n            and delta - rest >= ten_k\n            and (rest + ten_k < dist or dist - rest > rest + ten_k - dist))\n    {\n        assert(buf[len - 1] != '0');\n        buf[len - 1]--;\n        rest += ten_k;\n    }\n}\n\n/*!\nGenerates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.\nM- and M+ must be normalized and share the same exponent -60 <= e <= -32.\n*/\ninline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent,\n                             diyfp M_minus, diyfp w, diyfp M_plus)\n{\n    static_assert(kAlpha >= -60, \"internal error\");\n    static_assert(kGamma <= -32, \"internal error\");\n\n    // Generates the digits (and the exponent) of a decimal floating-point\n    // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's\n    // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma.\n    //\n    //               <--------------------------- delta ---->\n    //                                  <---- dist --------->\n    // --------------[------------------+-------------------]--------------\n    //               M-                 w                   M+\n    //\n    // Grisu2 generates the digits of M+ from left to right and stops as soon as\n    // V is in [M-,M+].\n\n    assert(M_plus.e >= kAlpha);\n    assert(M_plus.e <= kGamma);\n\n    uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e)\n    uint64_t dist  = diyfp::sub(M_plus, w      ).f; // (significand of (M+ - w ), implicit exponent is e)\n\n    // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):\n    //\n    //      M+ = f * 2^e\n    //         = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e\n    //         = ((p1        ) * 2^-e + (p2        )) * 2^e\n    //         = p1 + p2 * 2^e\n\n    const diyfp one(uint64_t{1} << -M_plus.e, M_plus.e);\n\n    auto p1 = static_cast<uint32_t>(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)\n    uint64_t p2 = M_plus.f & (one.f - 1);                    // p2 = f mod 2^-e\n\n    // 1)\n    //\n    // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]\n\n    assert(p1 > 0);\n\n    uint32_t pow10;\n    const int k = find_largest_pow10(p1, pow10);\n\n    //      10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)\n    //\n    //      p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))\n    //         = (d[k-1]         ) * 10^(k-1) + (p1 mod 10^(k-1))\n    //\n    //      M+ = p1                                             + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1))          + p2 * 2^e\n    //         = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e\n    //         = d[k-1] * 10^(k-1) + (                         rest) * 2^e\n    //\n    // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)\n    //\n    //      p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]\n    //\n    // but stop as soon as\n    //\n    //      rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e\n\n    int n = k;\n    while (n > 0)\n    {\n        // Invariants:\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)    (buffer = 0 for n = k)\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        //\n        const uint32_t d = p1 / pow10;  // d = p1 div 10^(n-1)\n        const uint32_t r = p1 % pow10;  // r = p1 mod 10^(n-1)\n        //\n        //      M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e\n        //         = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)\n        //\n        assert(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(n-1) + (r + p2 * 2^e)\n        //\n        p1 = r;\n        n--;\n        //\n        //      M+ = buffer * 10^n + (p1 + p2 * 2^e)\n        //      pow10 = 10^n\n        //\n\n        // Now check if enough digits have been generated.\n        // Compute\n        //\n        //      p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e\n        //\n        // Note:\n        // Since rest and delta share the same exponent e, it suffices to\n        // compare the significands.\n        const uint64_t rest = (uint64_t{p1} << -one.e) + p2;\n        if (rest <= delta)\n        {\n            // V = buffer * 10^n, with M- <= V <= M+.\n\n            decimal_exponent += n;\n\n            // We may now just stop. But instead look if the buffer could be\n            // decremented to bring V closer to w.\n            //\n            // pow10 = 10^n is now 1 ulp in the decimal representation V.\n            // The rounding procedure works with diyfp's with an implicit\n            // exponent of e.\n            //\n            //      10^n = (10^n * 2^-e) * 2^e = ulp * 2^e\n            //\n            const uint64_t ten_n = uint64_t{pow10} << -one.e;\n            grisu2_round(buffer, length, dist, delta, rest, ten_n);\n\n            return;\n        }\n\n        pow10 /= 10;\n        //\n        //      pow10 = 10^(n-1) <= p1 < 10^n\n        // Invariants restored.\n    }\n\n    // 2)\n    //\n    // The digits of the integral part have been generated:\n    //\n    //      M+ = d[k-1]...d[1]d[0] + p2 * 2^e\n    //         = buffer            + p2 * 2^e\n    //\n    // Now generate the digits of the fractional part p2 * 2^e.\n    //\n    // Note:\n    // No decimal point is generated: the exponent is adjusted instead.\n    //\n    // p2 actually represents the fraction\n    //\n    //      p2 * 2^e\n    //          = p2 / 2^-e\n    //          = d[-1] / 10^1 + d[-2] / 10^2 + ...\n    //\n    // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)\n    //\n    //      p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m\n    //                      + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)\n    //\n    // using\n    //\n    //      10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)\n    //                = (                   d) * 2^-e + (                   r)\n    //\n    // or\n    //      10^m * p2 * 2^e = d + r * 2^e\n    //\n    // i.e.\n    //\n    //      M+ = buffer + p2 * 2^e\n    //         = buffer + 10^-m * (d + r * 2^e)\n    //         = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e\n    //\n    // and stop as soon as 10^-m * r * 2^e <= delta * 2^e\n\n    assert(p2 > delta);\n\n    int m = 0;\n    for (;;)\n    {\n        // Invariant:\n        //      M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e\n        //         = buffer * 10^-m + 10^-m * (p2                                 ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (10 * p2)                   ) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e\n        //\n        assert(p2 <= UINT64_MAX / 10);\n        p2 *= 10;\n        const uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e\n        const uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e\n        //\n        //      M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e\n        //         = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))\n        //         = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        assert(d <= 9);\n        buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d\n        //\n        //      M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e\n        //\n        p2 = r;\n        m++;\n        //\n        //      M+ = buffer * 10^-m + 10^-m * p2 * 2^e\n        // Invariant restored.\n\n        // Check if enough digits have been generated.\n        //\n        //      10^-m * p2 * 2^e <= delta * 2^e\n        //              p2 * 2^e <= 10^m * delta * 2^e\n        //                    p2 <= 10^m * delta\n        delta *= 10;\n        dist  *= 10;\n        if (p2 <= delta)\n        {\n            break;\n        }\n    }\n\n    // V = buffer * 10^-m, with M- <= V <= M+.\n\n    decimal_exponent -= m;\n\n    // 1 ulp in the decimal representation is now 10^-m.\n    // Since delta and dist are now scaled by 10^m, we need to do the\n    // same with ulp in order to keep the units in sync.\n    //\n    //      10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e\n    //\n    const uint64_t ten_m = one.f;\n    grisu2_round(buffer, length, dist, delta, p2, ten_m);\n\n    // By construction this algorithm generates the shortest possible decimal\n    // number (Loitsch, Theorem 6.2) which rounds back to w.\n    // For an input number of precision p, at least\n    //\n    //      N = 1 + ceil(p * log_10(2))\n    //\n    // decimal digits are sufficient to identify all binary floating-point\n    // numbers (Matula, \"In-and-Out conversions\").\n    // This implies that the algorithm does not produce more than N decimal\n    // digits.\n    //\n    //      N = 17 for p = 53 (IEEE double precision)\n    //      N = 9  for p = 24 (IEEE single precision)\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\ninline void grisu2(char* buf, int& len, int& decimal_exponent,\n                   diyfp m_minus, diyfp v, diyfp m_plus)\n{\n    assert(m_plus.e == m_minus.e);\n    assert(m_plus.e == v.e);\n\n    //  --------(-----------------------+-----------------------)--------    (A)\n    //          m-                      v                       m+\n    //\n    //  --------------------(-----------+-----------------------)--------    (B)\n    //                      m-          v                       m+\n    //\n    // First scale v (and m- and m+) such that the exponent is in the range\n    // [alpha, gamma].\n\n    const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);\n\n    const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k\n\n    // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma]\n    const diyfp w       = diyfp::mul(v,       c_minus_k);\n    const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);\n    const diyfp w_plus  = diyfp::mul(m_plus,  c_minus_k);\n\n    //  ----(---+---)---------------(---+---)---------------(---+---)----\n    //          w-                      w                       w+\n    //          = c*m-                  = c*v                   = c*m+\n    //\n    // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and\n    // w+ are now off by a small amount.\n    // In fact:\n    //\n    //      w - v * 10^k < 1 ulp\n    //\n    // To account for this inaccuracy, add resp. subtract 1 ulp.\n    //\n    //  --------+---[---------------(---+---)---------------]---+--------\n    //          w-  M-                  w                   M+  w+\n    //\n    // Now any number in [M-, M+] (bounds included) will round to w when input,\n    // regardless of how the input rounding algorithm breaks ties.\n    //\n    // And digit_gen generates the shortest possible such number in [M-, M+].\n    // Note that this does not mean that Grisu2 always generates the shortest\n    // possible number in the interval (m-, m+).\n    const diyfp M_minus(w_minus.f + 1, w_minus.e);\n    const diyfp M_plus (w_plus.f  - 1, w_plus.e );\n\n    decimal_exponent = -cached.k; // = -(-k) = k\n\n    grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);\n}\n\n/*!\nv = buf * 10^decimal_exponent\nlen is the length of the buffer (number of decimal digits)\nThe buffer must be large enough, i.e. >= max_digits10.\n*/\ntemplate <typename FloatType>\nvoid grisu2(char* buf, int& len, int& decimal_exponent, FloatType value)\n{\n    static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,\n                  \"internal error: not enough precision\");\n\n    assert(std::isfinite(value));\n    assert(value > 0);\n\n    // If the neighbors (and boundaries) of 'value' are always computed for double-precision\n    // numbers, all float's can be recovered using strtod (and strtof). However, the resulting\n    // decimal representations are not exactly \"short\".\n    //\n    // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars)\n    // says \"value is converted to a string as if by std::sprintf in the default (\"C\") locale\"\n    // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars'\n    // does.\n    // On the other hand, the documentation for 'std::to_chars' requires that \"parsing the\n    // representation using the corresponding std::from_chars function recovers value exactly\". That\n    // indicates that single precision floating-point numbers should be recovered using\n    // 'std::strtof'.\n    //\n    // NB: If the neighbors are computed for single-precision numbers, there is a single float\n    //     (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision\n    //     value is off by 1 ulp.\n#if 0\n    const boundaries w = compute_boundaries(static_cast<double>(value));\n#else\n    const boundaries w = compute_boundaries(value);\n#endif\n\n    grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);\n}\n\n/*!\n@brief appends a decimal representation of e to buf\n@return a pointer to the element following the exponent.\n@pre -1000 < e < 1000\n*/\ninline char* append_exponent(char* buf, int e)\n{\n    assert(e > -1000);\n    assert(e <  1000);\n\n    if (e < 0)\n    {\n        e = -e;\n        *buf++ = '-';\n    }\n    else\n    {\n        *buf++ = '+';\n    }\n\n    auto k = static_cast<uint32_t>(e);\n    if (k < 10)\n    {\n        // Always print at least two digits in the exponent.\n        // This is for compatibility with printf(\"%g\").\n        *buf++ = '0';\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else if (k < 100)\n    {\n        *buf++ = static_cast<char>('0' + k / 10);\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n    else\n    {\n        *buf++ = static_cast<char>('0' + k / 100);\n        k %= 100;\n        *buf++ = static_cast<char>('0' + k / 10);\n        k %= 10;\n        *buf++ = static_cast<char>('0' + k);\n    }\n\n    return buf;\n}\n\n/*!\n@brief prettify v = buf * 10^decimal_exponent\n\nIf v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point\nnotation. Otherwise it will be printed in exponential notation.\n\n@pre min_exp < 0\n@pre max_exp > 0\n*/\ninline char* format_buffer(char* buf, int len, int decimal_exponent,\n                           int min_exp, int max_exp)\n{\n    assert(min_exp < 0);\n    assert(max_exp > 0);\n\n    const int k = len;\n    const int n = len + decimal_exponent;\n\n    // v = buf * 10^(n-k)\n    // k is the length of the buffer (number of decimal digits)\n    // n is the position of the decimal point relative to the start of the buffer.\n\n    if (k <= n and n <= max_exp)\n    {\n        // digits[000]\n        // len <= max_exp + 2\n\n        std::memset(buf + k, '0', static_cast<size_t>(n - k));\n        // Make it look like a floating-point number (#362, #378)\n        buf[n + 0] = '.';\n        buf[n + 1] = '0';\n        return buf + (n + 2);\n    }\n\n    if (0 < n and n <= max_exp)\n    {\n        // dig.its\n        // len <= max_digits10 + 1\n\n        assert(k > n);\n\n        std::memmove(buf + (n + 1), buf + n, static_cast<size_t>(k - n));\n        buf[n] = '.';\n        return buf + (k + 1);\n    }\n\n    if (min_exp < n and n <= 0)\n    {\n        // 0.[000]digits\n        // len <= 2 + (-min_exp - 1) + max_digits10\n\n        std::memmove(buf + (2 + -n), buf, static_cast<size_t>(k));\n        buf[0] = '0';\n        buf[1] = '.';\n        std::memset(buf + 2, '0', static_cast<size_t>(-n));\n        return buf + (2 + (-n) + k);\n    }\n\n    if (k == 1)\n    {\n        // dE+123\n        // len <= 1 + 5\n\n        buf += 1;\n    }\n    else\n    {\n        // d.igitsE+123\n        // len <= max_digits10 + 1 + 5\n\n        std::memmove(buf + 2, buf + 1, static_cast<size_t>(k - 1));\n        buf[1] = '.';\n        buf += 1 + k;\n    }\n\n    *buf++ = 'e';\n    return append_exponent(buf, n - 1);\n}\n\n} // namespace dtoa_impl\n\n/*!\n@brief generates a decimal representation of the floating-point number value in [first, last).\n\nThe format of the resulting decimal representation is similar to printf's %g\nformat. Returns an iterator pointing past-the-end of the decimal representation.\n\n@note The input number must be finite, i.e. NaN's and Inf's are not supported.\n@note The buffer must be large enough.\n@note The result is NOT null-terminated.\n*/\ntemplate <typename FloatType>\nchar* to_chars(char* first, const char* last, FloatType value)\n{\n    static_cast<void>(last); // maybe unused - fix warning\n    assert(std::isfinite(value));\n\n    // Use signbit(value) instead of (value < 0) since signbit works for -0.\n    if (std::signbit(value))\n    {\n        value = -value;\n        *first++ = '-';\n    }\n\n    if (value == 0) // +-0\n    {\n        *first++ = '0';\n        // Make it look like a floating-point number (#362, #378)\n        *first++ = '.';\n        *first++ = '0';\n        return first;\n    }\n\n    assert(last - first >= std::numeric_limits<FloatType>::max_digits10);\n\n    // Compute v = buffer * 10^decimal_exponent.\n    // The decimal digits are stored in the buffer, which needs to be interpreted\n    // as an unsigned decimal integer.\n    // len is the length of the buffer, i.e. the number of decimal digits.\n    int len = 0;\n    int decimal_exponent = 0;\n    dtoa_impl::grisu2(first, len, decimal_exponent, value);\n\n    assert(len <= std::numeric_limits<FloatType>::max_digits10);\n\n    // Format the buffer like printf(\"%.*g\", prec, value)\n    constexpr int kMinExp = -4;\n    // Use digits10 here to increase compatibility with version 2.\n    constexpr int kMaxExp = std::numeric_limits<FloatType>::digits10;\n\n    assert(last - first >= kMaxExp + 2);\n    assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits<FloatType>::max_digits10);\n    assert(last - first >= std::numeric_limits<FloatType>::max_digits10 + 6);\n\n    return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);\n}\n\n} // namespace detail\n} // namespace nlohmann\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/meta/cpp_future.hpp>\n\n// #include <nlohmann/detail/output/binary_writer.hpp>\n\n// #include <nlohmann/detail/output/output_adapters.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\n///////////////////\n// serialization //\n///////////////////\n\n/// how to treat decoding errors\nenum class error_handler_t\n{\n    strict,  ///< throw a type_error exception in case of invalid UTF-8\n    replace, ///< replace invalid UTF-8 sequences with U+FFFD\n    ignore   ///< ignore invalid UTF-8 sequences\n};\n\ntemplate<typename BasicJsonType>\nclass serializer\n{\n    using string_t = typename BasicJsonType::string_t;\n    using number_float_t = typename BasicJsonType::number_float_t;\n    using number_integer_t = typename BasicJsonType::number_integer_t;\n    using number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n    static constexpr uint8_t UTF8_ACCEPT = 0;\n    static constexpr uint8_t UTF8_REJECT = 1;\n\n  public:\n    /*!\n    @param[in] s  output stream to serialize to\n    @param[in] ichar  indentation character to use\n    @param[in] error_handler_  how to react on decoding errors\n    */\n    serializer(output_adapter_t<char> s, const char ichar,\n               error_handler_t error_handler_ = error_handler_t::strict)\n        : o(std::move(s))\n        , loc(std::localeconv())\n        , thousands_sep(loc->thousands_sep == nullptr ? '\\0' : * (loc->thousands_sep))\n        , decimal_point(loc->decimal_point == nullptr ? '\\0' : * (loc->decimal_point))\n        , indent_char(ichar)\n        , indent_string(512, indent_char)\n        , error_handler(error_handler_)\n    {}\n\n    // delete because of pointer members\n    serializer(const serializer&) = delete;\n    serializer& operator=(const serializer&) = delete;\n    serializer(serializer&&) = delete;\n    serializer& operator=(serializer&&) = delete;\n    ~serializer() = default;\n\n    /*!\n    @brief internal implementation of the serialization function\n\n    This function is called by the public member function dump and organizes\n    the serialization internally. The indentation level is propagated as\n    additional parameter. In case of arrays and objects, the function is\n    called recursively.\n\n    - strings and object keys are escaped using `escape_string()`\n    - integer numbers are converted implicitly via `operator<<`\n    - floating-point numbers are converted to a string using `\"%g\"` format\n\n    @param[in] val             value to serialize\n    @param[in] pretty_print    whether the output shall be pretty-printed\n    @param[in] indent_step     the indent level\n    @param[in] current_indent  the current indent level (only used internally)\n    */\n    void dump(const BasicJsonType& val, const bool pretty_print,\n              const bool ensure_ascii,\n              const unsigned int indent_step,\n              const unsigned int current_indent = 0)\n    {\n        switch (val.m_type)\n        {\n            case value_t::object:\n            {\n                if (val.m_value.object->empty())\n                {\n                    o->write_characters(\"{}\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"{\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    auto i = val.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\": \", 3);\n                        dump(i->second, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    assert(i != val.m_value.object->cend());\n                    assert(std::next(i) == val.m_value.object->cend());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\": \", 3);\n                    dump(i->second, true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character('}');\n                }\n                else\n                {\n                    o->write_character('{');\n\n                    // first n-1 elements\n                    auto i = val.m_value.object->cbegin();\n                    for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i)\n                    {\n                        o->write_character('\\\"');\n                        dump_escaped(i->first, ensure_ascii);\n                        o->write_characters(\"\\\":\", 2);\n                        dump(i->second, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    assert(i != val.m_value.object->cend());\n                    assert(std::next(i) == val.m_value.object->cend());\n                    o->write_character('\\\"');\n                    dump_escaped(i->first, ensure_ascii);\n                    o->write_characters(\"\\\":\", 2);\n                    dump(i->second, false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character('}');\n                }\n\n                return;\n            }\n\n            case value_t::array:\n            {\n                if (val.m_value.array->empty())\n                {\n                    o->write_characters(\"[]\", 2);\n                    return;\n                }\n\n                if (pretty_print)\n                {\n                    o->write_characters(\"[\\n\", 2);\n\n                    // variable to hold indentation for recursive calls\n                    const auto new_indent = current_indent + indent_step;\n                    if (JSON_UNLIKELY(indent_string.size() < new_indent))\n                    {\n                        indent_string.resize(indent_string.size() * 2, ' ');\n                    }\n\n                    // first n-1 elements\n                    for (auto i = val.m_value.array->cbegin();\n                            i != val.m_value.array->cend() - 1; ++i)\n                    {\n                        o->write_characters(indent_string.c_str(), new_indent);\n                        dump(*i, true, ensure_ascii, indent_step, new_indent);\n                        o->write_characters(\",\\n\", 2);\n                    }\n\n                    // last element\n                    assert(not val.m_value.array->empty());\n                    o->write_characters(indent_string.c_str(), new_indent);\n                    dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent);\n\n                    o->write_character('\\n');\n                    o->write_characters(indent_string.c_str(), current_indent);\n                    o->write_character(']');\n                }\n                else\n                {\n                    o->write_character('[');\n\n                    // first n-1 elements\n                    for (auto i = val.m_value.array->cbegin();\n                            i != val.m_value.array->cend() - 1; ++i)\n                    {\n                        dump(*i, false, ensure_ascii, indent_step, current_indent);\n                        o->write_character(',');\n                    }\n\n                    // last element\n                    assert(not val.m_value.array->empty());\n                    dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent);\n\n                    o->write_character(']');\n                }\n\n                return;\n            }\n\n            case value_t::string:\n            {\n                o->write_character('\\\"');\n                dump_escaped(*val.m_value.string, ensure_ascii);\n                o->write_character('\\\"');\n                return;\n            }\n\n            case value_t::boolean:\n            {\n                if (val.m_value.boolean)\n                {\n                    o->write_characters(\"true\", 4);\n                }\n                else\n                {\n                    o->write_characters(\"false\", 5);\n                }\n                return;\n            }\n\n            case value_t::number_integer:\n            {\n                dump_integer(val.m_value.number_integer);\n                return;\n            }\n\n            case value_t::number_unsigned:\n            {\n                dump_integer(val.m_value.number_unsigned);\n                return;\n            }\n\n            case value_t::number_float:\n            {\n                dump_float(val.m_value.number_float);\n                return;\n            }\n\n            case value_t::discarded:\n            {\n                o->write_characters(\"<discarded>\", 11);\n                return;\n            }\n\n            case value_t::null:\n            {\n                o->write_characters(\"null\", 4);\n                return;\n            }\n        }\n    }\n\n  private:\n    /*!\n    @brief dump escaped string\n\n    Escape a string by replacing certain special characters by a sequence of an\n    escape character (backslash) and another character and other control\n    characters by a sequence of \"\\u\" followed by a four-digit hex\n    representation. The escaped string is written to output stream @a o.\n\n    @param[in] s  the string to escape\n    @param[in] ensure_ascii  whether to escape non-ASCII characters with\n                             \\uXXXX sequences\n\n    @complexity Linear in the length of string @a s.\n    */\n    void dump_escaped(const string_t& s, const bool ensure_ascii)\n    {\n        uint32_t codepoint;\n        uint8_t state = UTF8_ACCEPT;\n        std::size_t bytes = 0;  // number of bytes written to string_buffer\n\n        // number of bytes written at the point of the last valid byte\n        std::size_t bytes_after_last_accept = 0;\n        std::size_t undumped_chars = 0;\n\n        for (std::size_t i = 0; i < s.size(); ++i)\n        {\n            const auto byte = static_cast<uint8_t>(s[i]);\n\n            switch (decode(state, codepoint, byte))\n            {\n                case UTF8_ACCEPT:  // decode found a new code point\n                {\n                    switch (codepoint)\n                    {\n                        case 0x08: // backspace\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'b';\n                            break;\n                        }\n\n                        case 0x09: // horizontal tab\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 't';\n                            break;\n                        }\n\n                        case 0x0A: // newline\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'n';\n                            break;\n                        }\n\n                        case 0x0C: // formfeed\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'f';\n                            break;\n                        }\n\n                        case 0x0D: // carriage return\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = 'r';\n                            break;\n                        }\n\n                        case 0x22: // quotation mark\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\"';\n                            break;\n                        }\n\n                        case 0x5C: // reverse solidus\n                        {\n                            string_buffer[bytes++] = '\\\\';\n                            string_buffer[bytes++] = '\\\\';\n                            break;\n                        }\n\n                        default:\n                        {\n                            // escape control characters (0x00..0x1F) or, if\n                            // ensure_ascii parameter is used, non-ASCII characters\n                            if ((codepoint <= 0x1F) or (ensure_ascii and (codepoint >= 0x7F)))\n                            {\n                                if (codepoint <= 0xFFFF)\n                                {\n                                    std::snprintf(string_buffer.data() + bytes, 7, \"\\\\u%04x\",\n                                                  static_cast<uint16_t>(codepoint));\n                                    bytes += 6;\n                                }\n                                else\n                                {\n                                    std::snprintf(string_buffer.data() + bytes, 13, \"\\\\u%04x\\\\u%04x\",\n                                                  static_cast<uint16_t>(0xD7C0 + (codepoint >> 10)),\n                                                  static_cast<uint16_t>(0xDC00 + (codepoint & 0x3FF)));\n                                    bytes += 12;\n                                }\n                            }\n                            else\n                            {\n                                // copy byte to buffer (all previous bytes\n                                // been copied have in default case above)\n                                string_buffer[bytes++] = s[i];\n                            }\n                            break;\n                        }\n                    }\n\n                    // write buffer and reset index; there must be 13 bytes\n                    // left, as this is the maximal number of bytes to be\n                    // written (\"\\uxxxx\\uxxxx\\0\") for one code point\n                    if (string_buffer.size() - bytes < 13)\n                    {\n                        o->write_characters(string_buffer.data(), bytes);\n                        bytes = 0;\n                    }\n\n                    // remember the byte position of this accept\n                    bytes_after_last_accept = bytes;\n                    undumped_chars = 0;\n                    break;\n                }\n\n                case UTF8_REJECT:  // decode found invalid UTF-8 byte\n                {\n                    switch (error_handler)\n                    {\n                        case error_handler_t::strict:\n                        {\n                            std::string sn(3, '\\0');\n                            snprintf(&sn[0], sn.size(), \"%.2X\", byte);\n                            JSON_THROW(type_error::create(316, \"invalid UTF-8 byte at index \" + std::to_string(i) + \": 0x\" + sn));\n                        }\n\n                        case error_handler_t::ignore:\n                        case error_handler_t::replace:\n                        {\n                            // in case we saw this character the first time, we\n                            // would like to read it again, because the byte\n                            // may be OK for itself, but just not OK for the\n                            // previous sequence\n                            if (undumped_chars > 0)\n                            {\n                                --i;\n                            }\n\n                            // reset length buffer to the last accepted index;\n                            // thus removing/ignoring the invalid characters\n                            bytes = bytes_after_last_accept;\n\n                            if (error_handler == error_handler_t::replace)\n                            {\n                                // add a replacement character\n                                if (ensure_ascii)\n                                {\n                                    string_buffer[bytes++] = '\\\\';\n                                    string_buffer[bytes++] = 'u';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'f';\n                                    string_buffer[bytes++] = 'd';\n                                }\n                                else\n                                {\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xEF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBF');\n                                    string_buffer[bytes++] = detail::binary_writer<BasicJsonType, char>::to_char_type('\\xBD');\n                                }\n                                bytes_after_last_accept = bytes;\n                            }\n\n                            undumped_chars = 0;\n\n                            // continue processing the string\n                            state = UTF8_ACCEPT;\n                            break;\n                        }\n                    }\n                    break;\n                }\n\n                default:  // decode found yet incomplete multi-byte code point\n                {\n                    if (not ensure_ascii)\n                    {\n                        // code point will not be escaped - copy byte to buffer\n                        string_buffer[bytes++] = s[i];\n                    }\n                    ++undumped_chars;\n                    break;\n                }\n            }\n        }\n\n        // we finished processing the string\n        if (JSON_LIKELY(state == UTF8_ACCEPT))\n        {\n            // write buffer\n            if (bytes > 0)\n            {\n                o->write_characters(string_buffer.data(), bytes);\n            }\n        }\n        else\n        {\n            // we finish reading, but do not accept: string was incomplete\n            switch (error_handler)\n            {\n                case error_handler_t::strict:\n                {\n                    std::string sn(3, '\\0');\n                    snprintf(&sn[0], sn.size(), \"%.2X\", static_cast<uint8_t>(s.back()));\n                    JSON_THROW(type_error::create(316, \"incomplete UTF-8 string; last byte: 0x\" + sn));\n                }\n\n                case error_handler_t::ignore:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    break;\n                }\n\n                case error_handler_t::replace:\n                {\n                    // write all accepted bytes\n                    o->write_characters(string_buffer.data(), bytes_after_last_accept);\n                    // add a replacement character\n                    if (ensure_ascii)\n                    {\n                        o->write_characters(\"\\\\ufffd\", 6);\n                    }\n                    else\n                    {\n                        o->write_characters(\"\\xEF\\xBF\\xBD\", 3);\n                    }\n                    break;\n                }\n            }\n        }\n    }\n\n    /*!\n    @brief dump an integer\n\n    Dump a given integer to output stream @a o. Works internally with\n    @a number_buffer.\n\n    @param[in] x  integer number (signed or unsigned) to dump\n    @tparam NumberType either @a number_integer_t or @a number_unsigned_t\n    */\n    template<typename NumberType, detail::enable_if_t<\n                 std::is_same<NumberType, number_unsigned_t>::value or\n                 std::is_same<NumberType, number_integer_t>::value,\n                 int> = 0>\n    void dump_integer(NumberType x)\n    {\n        // special case for \"0\"\n        if (x == 0)\n        {\n            o->write_character('0');\n            return;\n        }\n\n        const bool is_negative = std::is_same<NumberType, number_integer_t>::value and not (x >= 0);  // see issue #755\n        std::size_t i = 0;\n\n        while (x != 0)\n        {\n            // spare 1 byte for '\\0'\n            assert(i < number_buffer.size() - 1);\n\n            const auto digit = std::labs(static_cast<long>(x % 10));\n            number_buffer[i++] = static_cast<char>('0' + digit);\n            x /= 10;\n        }\n\n        if (is_negative)\n        {\n            // make sure there is capacity for the '-'\n            assert(i < number_buffer.size() - 2);\n            number_buffer[i++] = '-';\n        }\n\n        std::reverse(number_buffer.begin(), number_buffer.begin() + i);\n        o->write_characters(number_buffer.data(), i);\n    }\n\n    /*!\n    @brief dump a floating-point number\n\n    Dump a given floating-point number to output stream @a o. Works internally\n    with @a number_buffer.\n\n    @param[in] x  floating-point number to dump\n    */\n    void dump_float(number_float_t x)\n    {\n        // NaN / inf\n        if (not std::isfinite(x))\n        {\n            o->write_characters(\"null\", 4);\n            return;\n        }\n\n        // If number_float_t is an IEEE-754 single or double precision number,\n        // use the Grisu2 algorithm to produce short numbers which are\n        // guaranteed to round-trip, using strtof and strtod, resp.\n        //\n        // NB: The test below works if <long double> == <double>.\n        static constexpr bool is_ieee_single_or_double\n            = (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 24 and std::numeric_limits<number_float_t>::max_exponent == 128) or\n              (std::numeric_limits<number_float_t>::is_iec559 and std::numeric_limits<number_float_t>::digits == 53 and std::numeric_limits<number_float_t>::max_exponent == 1024);\n\n        dump_float(x, std::integral_constant<bool, is_ieee_single_or_double>());\n    }\n\n    void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/)\n    {\n        char* begin = number_buffer.data();\n        char* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x);\n\n        o->write_characters(begin, static_cast<size_t>(end - begin));\n    }\n\n    void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)\n    {\n        // get number of digits for a float -> text -> float round-trip\n        static constexpr auto d = std::numeric_limits<number_float_t>::max_digits10;\n\n        // the actual conversion\n        std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), \"%.*g\", d, x);\n\n        // negative value indicates an error\n        assert(len > 0);\n        // check if buffer was large enough\n        assert(static_cast<std::size_t>(len) < number_buffer.size());\n\n        // erase thousands separator\n        if (thousands_sep != '\\0')\n        {\n            const auto end = std::remove(number_buffer.begin(),\n                                         number_buffer.begin() + len, thousands_sep);\n            std::fill(end, number_buffer.end(), '\\0');\n            assert((end - number_buffer.begin()) <= len);\n            len = (end - number_buffer.begin());\n        }\n\n        // convert decimal point to '.'\n        if (decimal_point != '\\0' and decimal_point != '.')\n        {\n            const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point);\n            if (dec_pos != number_buffer.end())\n            {\n                *dec_pos = '.';\n            }\n        }\n\n        o->write_characters(number_buffer.data(), static_cast<std::size_t>(len));\n\n        // determine if need to append \".0\"\n        const bool value_is_int_like =\n            std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1,\n                         [](char c)\n        {\n            return (c == '.' or c == 'e');\n        });\n\n        if (value_is_int_like)\n        {\n            o->write_characters(\".0\", 2);\n        }\n    }\n\n    /*!\n    @brief check whether a string is UTF-8 encoded\n\n    The function checks each byte of a string whether it is UTF-8 encoded. The\n    result of the check is stored in the @a state parameter. The function must\n    be called initially with state 0 (accept). State 1 means the string must\n    be rejected, because the current byte is not allowed. If the string is\n    completely processed, but the state is non-zero, the string ended\n    prematurely; that is, the last byte indicated more bytes should have\n    followed.\n\n    @param[in,out] state  the state of the decoding\n    @param[in,out] codep  codepoint (valid only if resulting state is UTF8_ACCEPT)\n    @param[in] byte       next byte to decode\n    @return               new state\n\n    @note The function has been edited: a std::array is used.\n\n    @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>\n    @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/\n    */\n    static uint8_t decode(uint8_t& state, uint32_t& codep, const uint8_t byte) noexcept\n    {\n        static const std::array<uint8_t, 400> utf8d =\n        {\n            {\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F\n                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F\n                7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF\n                8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF\n                0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF\n                0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF\n                0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0\n                1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2\n                1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4\n                1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6\n                1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8\n            }\n        };\n\n        const uint8_t type = utf8d[byte];\n\n        codep = (state != UTF8_ACCEPT)\n                ? (byte & 0x3fu) | (codep << 6)\n                : static_cast<uint32_t>(0xff >> type) & (byte);\n\n        state = utf8d[256u + state * 16u + type];\n        return state;\n    }\n\n  private:\n    /// the output of the serializer\n    output_adapter_t<char> o = nullptr;\n\n    /// a (hopefully) large enough character buffer\n    std::array<char, 64> number_buffer{{}};\n\n    /// the locale\n    const std::lconv* loc = nullptr;\n    /// the locale's thousand separator character\n    const char thousands_sep = '\\0';\n    /// the locale's decimal point character\n    const char decimal_point = '\\0';\n\n    /// string buffer\n    std::array<char, 512> string_buffer{{}};\n\n    /// the indentation character\n    const char indent_char;\n    /// the indentation string\n    string_t indent_string;\n\n    /// error_handler how to react on decoding errors\n    const error_handler_t error_handler;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/json_ref.hpp>\n\n\n#include <initializer_list>\n#include <utility>\n\n// #include <nlohmann/detail/meta/type_traits.hpp>\n\n\nnamespace nlohmann\n{\nnamespace detail\n{\ntemplate<typename BasicJsonType>\nclass json_ref\n{\n  public:\n    using value_type = BasicJsonType;\n\n    json_ref(value_type&& value)\n        : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true)\n    {}\n\n    json_ref(const value_type& value)\n        : value_ref(const_cast<value_type*>(&value)), is_rvalue(false)\n    {}\n\n    json_ref(std::initializer_list<json_ref> init)\n        : owned_value(init), value_ref(&owned_value), is_rvalue(true)\n    {}\n\n    template <\n        class... Args,\n        enable_if_t<std::is_constructible<value_type, Args...>::value, int> = 0 >\n    json_ref(Args && ... args)\n        : owned_value(std::forward<Args>(args)...), value_ref(&owned_value),\n          is_rvalue(true) {}\n\n    // class should be movable only\n    json_ref(json_ref&&) = default;\n    json_ref(const json_ref&) = delete;\n    json_ref& operator=(const json_ref&) = delete;\n    json_ref& operator=(json_ref&&) = delete;\n    ~json_ref() = default;\n\n    value_type moved_or_copied() const\n    {\n        if (is_rvalue)\n        {\n            return std::move(*value_ref);\n        }\n        return *value_ref;\n    }\n\n    value_type const& operator*() const\n    {\n        return *static_cast<value_type const*>(value_ref);\n    }\n\n    value_type const* operator->() const\n    {\n        return static_cast<value_type const*>(value_ref);\n    }\n\n  private:\n    mutable value_type owned_value = nullptr;\n    value_type* value_ref = nullptr;\n    const bool is_rvalue;\n};\n}  // namespace detail\n}  // namespace nlohmann\n\n// #include <nlohmann/detail/json_pointer.hpp>\n\n\n#include <cassert> // assert\n#include <numeric> // accumulate\n#include <string> // string\n#include <vector> // vector\n\n// #include <nlohmann/detail/macro_scope.hpp>\n\n// #include <nlohmann/detail/exceptions.hpp>\n\n// #include <nlohmann/detail/value_t.hpp>\n\n\nnamespace nlohmann\n{\ntemplate<typename BasicJsonType>\nclass json_pointer\n{\n    // allow basic_json to access private members\n    NLOHMANN_BASIC_JSON_TPL_DECLARATION\n    friend class basic_json;\n\n  public:\n    /*!\n    @brief create JSON pointer\n\n    Create a JSON pointer according to the syntax described in\n    [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).\n\n    @param[in] s  string representing the JSON pointer; if omitted, the empty\n                  string is assumed which references the whole JSON value\n\n    @throw parse_error.107 if the given JSON pointer @a s is nonempty and does\n                           not begin with a slash (`/`); see example below\n\n    @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is\n    not followed by `0` (representing `~`) or `1` (representing `/`); see\n    example below\n\n    @liveexample{The example shows the construction several valid JSON pointers\n    as well as the exceptional behavior.,json_pointer}\n\n    @since version 2.0.0\n    */\n    explicit json_pointer(const std::string& s = \"\")\n        : reference_tokens(split(s))\n    {}\n\n    /*!\n    @brief return a string representation of the JSON pointer\n\n    @invariant For each JSON pointer `ptr`, it holds:\n    @code {.cpp}\n    ptr == json_pointer(ptr.to_string());\n    @endcode\n\n    @return a string representation of the JSON pointer\n\n    @liveexample{The example shows the result of `to_string`.,\n    json_pointer__to_string}\n\n    @since version 2.0.0\n    */\n    std::string to_string() const\n    {\n        return std::accumulate(reference_tokens.begin(), reference_tokens.end(),\n                               std::string{},\n                               [](const std::string & a, const std::string & b)\n        {\n            return a + \"/\" + escape(b);\n        });\n    }\n\n    /// @copydoc to_string()\n    operator std::string() const\n    {\n        return to_string();\n    }\n\n    /*!\n    @param[in] s  reference token to be converted into an array index\n\n    @return integer representation of @a s\n\n    @throw out_of_range.404 if string @a s could not be converted to an integer\n    */\n    static int array_index(const std::string& s)\n    {\n        std::size_t processed_chars = 0;\n        const int res = std::stoi(s, &processed_chars);\n\n        // check if the string was completely read\n        if (JSON_UNLIKELY(processed_chars != s.size()))\n        {\n            JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + s + \"'\"));\n        }\n\n        return res;\n    }\n\n  private:\n    /*!\n    @brief remove and return last reference pointer\n    @throw out_of_range.405 if JSON pointer has no parent\n    */\n    std::string pop_back()\n    {\n        if (JSON_UNLIKELY(is_root()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\"));\n        }\n\n        auto last = reference_tokens.back();\n        reference_tokens.pop_back();\n        return last;\n    }\n\n    /// return whether pointer points to the root document\n    bool is_root() const noexcept\n    {\n        return reference_tokens.empty();\n    }\n\n    json_pointer top() const\n    {\n        if (JSON_UNLIKELY(is_root()))\n        {\n            JSON_THROW(detail::out_of_range::create(405, \"JSON pointer has no parent\"));\n        }\n\n        json_pointer result = *this;\n        result.reference_tokens = {reference_tokens[0]};\n        return result;\n    }\n\n    /*!\n    @brief create and return a reference to the pointed to value\n\n    @complexity Linear in the number of reference tokens.\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.313 if value cannot be unflattened\n    */\n    BasicJsonType& get_and_create(BasicJsonType& j) const\n    {\n        using size_type = typename BasicJsonType::size_type;\n        auto result = &j;\n\n        // in case no reference tokens exist, return a reference to the JSON value\n        // j which will be overwritten by a primitive value\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (result->m_type)\n            {\n                case detail::value_t::null:\n                {\n                    if (reference_token == \"0\")\n                    {\n                        // start a new array if reference token is 0\n                        result = &result->operator[](0);\n                    }\n                    else\n                    {\n                        // start a new object otherwise\n                        result = &result->operator[](reference_token);\n                    }\n                    break;\n                }\n\n                case detail::value_t::object:\n                {\n                    // create an entry in the object\n                    result = &result->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    // create an entry in the array\n                    JSON_TRY\n                    {\n                        result = &result->operator[](static_cast<size_type>(array_index(reference_token)));\n                    }\n                    JSON_CATCH(std::invalid_argument&)\n                    {\n                        JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + reference_token + \"' is not a number\"));\n                    }\n                    break;\n                }\n\n                /*\n                The following code is only reached if there exists a reference\n                token _and_ the current value is primitive. In this case, we have\n                an error situation, because primitive values may only occur as\n                single value; that is, with an empty list of reference tokens.\n                */\n                default:\n                    JSON_THROW(detail::type_error::create(313, \"invalid value to unflatten\"));\n            }\n        }\n\n        return *result;\n    }\n\n    /*!\n    @brief return a reference to the pointed to value\n\n    @note This version does not throw if a value is not present, but tries to\n          create nested values instead. For instance, calling this function\n          with pointer `\"/this/that\"` on a null value is equivalent to calling\n          `operator[](\"this\").operator[](\"that\")` on that value, effectively\n          changing the null value to an object.\n\n    @param[in] ptr  a JSON value\n\n    @return reference to the JSON value pointed to by the JSON pointer\n\n    @complexity Linear in the length of the JSON pointer.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    BasicJsonType& get_unchecked(BasicJsonType* ptr) const\n    {\n        using size_type = typename BasicJsonType::size_type;\n        for (const auto& reference_token : reference_tokens)\n        {\n            // convert null values to arrays or objects before continuing\n            if (ptr->m_type == detail::value_t::null)\n            {\n                // check if reference token is a number\n                const bool nums =\n                    std::all_of(reference_token.begin(), reference_token.end(),\n                                [](const char x)\n                {\n                    return (x >= '0' and x <= '9');\n                });\n\n                // change value to array for numbers or \"-\" or to object otherwise\n                *ptr = (nums or reference_token == \"-\")\n                       ? detail::value_t::array\n                       : detail::value_t::object;\n            }\n\n            switch (ptr->m_type)\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    // error condition (cf. RFC 6901, Sect. 4)\n                    if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))\n                    {\n                        JSON_THROW(detail::parse_error::create(106, 0,\n                                                               \"array index '\" + reference_token +\n                                                               \"' must not begin with '0'\"));\n                    }\n\n                    if (reference_token == \"-\")\n                    {\n                        // explicitly treat \"-\" as index beyond the end\n                        ptr = &ptr->operator[](ptr->m_value.array->size());\n                    }\n                    else\n                    {\n                        // convert array index to number; unchecked access\n                        JSON_TRY\n                        {\n                            ptr = &ptr->operator[](\n                                static_cast<size_type>(array_index(reference_token)));\n                        }\n                        JSON_CATCH(std::invalid_argument&)\n                        {\n                            JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + reference_token + \"' is not a number\"));\n                        }\n                    }\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    BasicJsonType& get_checked(BasicJsonType* ptr) const\n    {\n        using size_type = typename BasicJsonType::size_type;\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->m_type)\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\"));\n                    }\n\n                    // error condition (cf. RFC 6901, Sect. 4)\n                    if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))\n                    {\n                        JSON_THROW(detail::parse_error::create(106, 0,\n                                                               \"array index '\" + reference_token +\n                                                               \"' must not begin with '0'\"));\n                    }\n\n                    // note: at performs range check\n                    JSON_TRY\n                    {\n                        ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));\n                    }\n                    JSON_CATCH(std::invalid_argument&)\n                    {\n                        JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + reference_token + \"' is not a number\"));\n                    }\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @brief return a const reference to the pointed to value\n\n    @param[in] ptr  a JSON value\n\n    @return const reference to the JSON value pointed to by the JSON\n    pointer\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const\n    {\n        using size_type = typename BasicJsonType::size_type;\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->m_type)\n            {\n                case detail::value_t::object:\n                {\n                    // use unchecked object access\n                    ptr = &ptr->operator[](reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" cannot be used for const access\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\"));\n                    }\n\n                    // error condition (cf. RFC 6901, Sect. 4)\n                    if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))\n                    {\n                        JSON_THROW(detail::parse_error::create(106, 0,\n                                                               \"array index '\" + reference_token +\n                                                               \"' must not begin with '0'\"));\n                    }\n\n                    // use unchecked array access\n                    JSON_TRY\n                    {\n                        ptr = &ptr->operator[](\n                            static_cast<size_type>(array_index(reference_token)));\n                    }\n                    JSON_CATCH(std::invalid_argument&)\n                    {\n                        JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + reference_token + \"' is not a number\"));\n                    }\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n    */\n    const BasicJsonType& get_checked(const BasicJsonType* ptr) const\n    {\n        using size_type = typename BasicJsonType::size_type;\n        for (const auto& reference_token : reference_tokens)\n        {\n            switch (ptr->m_type)\n            {\n                case detail::value_t::object:\n                {\n                    // note: at performs range check\n                    ptr = &ptr->at(reference_token);\n                    break;\n                }\n\n                case detail::value_t::array:\n                {\n                    if (JSON_UNLIKELY(reference_token == \"-\"))\n                    {\n                        // \"-\" always fails the range check\n                        JSON_THROW(detail::out_of_range::create(402,\n                                                                \"array index '-' (\" + std::to_string(ptr->m_value.array->size()) +\n                                                                \") is out of range\"));\n                    }\n\n                    // error condition (cf. RFC 6901, Sect. 4)\n                    if (JSON_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0'))\n                    {\n                        JSON_THROW(detail::parse_error::create(106, 0,\n                                                               \"array index '\" + reference_token +\n                                                               \"' must not begin with '0'\"));\n                    }\n\n                    // note: at performs range check\n                    JSON_TRY\n                    {\n                        ptr = &ptr->at(static_cast<size_type>(array_index(reference_token)));\n                    }\n                    JSON_CATCH(std::invalid_argument&)\n                    {\n                        JSON_THROW(detail::parse_error::create(109, 0, \"array index '\" + reference_token + \"' is not a number\"));\n                    }\n                    break;\n                }\n\n                default:\n                    JSON_THROW(detail::out_of_range::create(404, \"unresolved reference token '\" + reference_token + \"'\"));\n            }\n        }\n\n        return *ptr;\n    }\n\n    /*!\n    @brief split the string input to reference tokens\n\n    @note This function is only called by the json_pointer constructor.\n          All exceptions below are documented there.\n\n    @throw parse_error.107  if the pointer is not empty or begins with '/'\n    @throw parse_error.108  if character '~' is not followed by '0' or '1'\n    */\n    static std::vector<std::string> split(const std::string& reference_string)\n    {\n        std::vector<std::string> result;\n\n        // special case: empty reference string -> no reference tokens\n        if (reference_string.empty())\n        {\n            return result;\n        }\n\n        // check if nonempty reference string begins with slash\n        if (JSON_UNLIKELY(reference_string[0] != '/'))\n        {\n            JSON_THROW(detail::parse_error::create(107, 1,\n                                                   \"JSON pointer must be empty or begin with '/' - was: '\" +\n                                                   reference_string + \"'\"));\n        }\n\n        // extract the reference tokens:\n        // - slash: position of the last read slash (or end of string)\n        // - start: position after the previous slash\n        for (\n            // search for the first slash after the first character\n            std::size_t slash = reference_string.find_first_of('/', 1),\n            // set the beginning of the first reference token\n            start = 1;\n            // we can stop if start == 0 (if slash == std::string::npos)\n            start != 0;\n            // set the beginning of the next reference token\n            // (will eventually be 0 if slash == std::string::npos)\n            start = (slash == std::string::npos) ? 0 : slash + 1,\n            // find next slash\n            slash = reference_string.find_first_of('/', start))\n        {\n            // use the text between the beginning of the reference token\n            // (start) and the last slash (slash).\n            auto reference_token = reference_string.substr(start, slash - start);\n\n            // check reference tokens are properly escaped\n            for (std::size_t pos = reference_token.find_first_of('~');\n                    pos != std::string::npos;\n                    pos = reference_token.find_first_of('~', pos + 1))\n            {\n                assert(reference_token[pos] == '~');\n\n                // ~ must be followed by 0 or 1\n                if (JSON_UNLIKELY(pos == reference_token.size() - 1 or\n                                  (reference_token[pos + 1] != '0' and\n                                   reference_token[pos + 1] != '1')))\n                {\n                    JSON_THROW(detail::parse_error::create(108, 0, \"escape character '~' must be followed with '0' or '1'\"));\n                }\n            }\n\n            // finally, store the reference token\n            unescape(reference_token);\n            result.push_back(reference_token);\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief replace all occurrences of a substring by another string\n\n    @param[in,out] s  the string to manipulate; changed so that all\n                   occurrences of @a f are replaced with @a t\n    @param[in]     f  the substring to replace with @a t\n    @param[in]     t  the string to replace @a f\n\n    @pre The search string @a f must not be empty. **This precondition is\n    enforced with an assertion.**\n\n    @since version 2.0.0\n    */\n    static void replace_substring(std::string& s, const std::string& f,\n                                  const std::string& t)\n    {\n        assert(not f.empty());\n        for (auto pos = s.find(f);                // find first occurrence of f\n                pos != std::string::npos;         // make sure f was found\n                s.replace(pos, f.size(), t),      // replace with t, and\n                pos = s.find(f, pos + t.size()))  // find next occurrence of f\n        {}\n    }\n\n    /// escape \"~\" to \"~0\" and \"/\" to \"~1\"\n    static std::string escape(std::string s)\n    {\n        replace_substring(s, \"~\", \"~0\");\n        replace_substring(s, \"/\", \"~1\");\n        return s;\n    }\n\n    /// unescape \"~1\" to tilde and \"~0\" to slash (order is important!)\n    static void unescape(std::string& s)\n    {\n        replace_substring(s, \"~1\", \"/\");\n        replace_substring(s, \"~0\", \"~\");\n    }\n\n    /*!\n    @param[in] reference_string  the reference string to the current value\n    @param[in] value             the value to consider\n    @param[in,out] result        the result object to insert values to\n\n    @note Empty objects or arrays are flattened to `null`.\n    */\n    static void flatten(const std::string& reference_string,\n                        const BasicJsonType& value,\n                        BasicJsonType& result)\n    {\n        switch (value.m_type)\n        {\n            case detail::value_t::array:\n            {\n                if (value.m_value.array->empty())\n                {\n                    // flatten empty array as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate array and use index as reference string\n                    for (std::size_t i = 0; i < value.m_value.array->size(); ++i)\n                    {\n                        flatten(reference_string + \"/\" + std::to_string(i),\n                                value.m_value.array->operator[](i), result);\n                    }\n                }\n                break;\n            }\n\n            case detail::value_t::object:\n            {\n                if (value.m_value.object->empty())\n                {\n                    // flatten empty object as null\n                    result[reference_string] = nullptr;\n                }\n                else\n                {\n                    // iterate object and use keys as reference string\n                    for (const auto& element : *value.m_value.object)\n                    {\n                        flatten(reference_string + \"/\" + escape(element.first), element.second, result);\n                    }\n                }\n                break;\n            }\n\n            default:\n            {\n                // add primitive value with its reference string\n                result[reference_string] = value;\n                break;\n            }\n        }\n    }\n\n    /*!\n    @param[in] value  flattened JSON\n\n    @return unflattened JSON\n\n    @throw parse_error.109 if array index is not a number\n    @throw type_error.314  if value is not an object\n    @throw type_error.315  if object values are not primitive\n    @throw type_error.313  if value cannot be unflattened\n    */\n    static BasicJsonType\n    unflatten(const BasicJsonType& value)\n    {\n        if (JSON_UNLIKELY(not value.is_object()))\n        {\n            JSON_THROW(detail::type_error::create(314, \"only objects can be unflattened\"));\n        }\n\n        BasicJsonType result;\n\n        // iterate the JSON object values\n        for (const auto& element : *value.m_value.object)\n        {\n            if (JSON_UNLIKELY(not element.second.is_primitive()))\n            {\n                JSON_THROW(detail::type_error::create(315, \"values in object must be primitive\"));\n            }\n\n            // assign value to reference pointed to by JSON pointer; Note that if\n            // the JSON pointer is \"\" (i.e., points to the whole value), function\n            // get_and_create returns a reference to result itself. An assignment\n            // will then create a primitive value.\n            json_pointer(element.first).get_and_create(result) = element.second;\n        }\n\n        return result;\n    }\n\n    friend bool operator==(json_pointer const& lhs,\n                           json_pointer const& rhs) noexcept\n    {\n        return (lhs.reference_tokens == rhs.reference_tokens);\n    }\n\n    friend bool operator!=(json_pointer const& lhs,\n                           json_pointer const& rhs) noexcept\n    {\n        return not (lhs == rhs);\n    }\n\n    /// the reference tokens\n    std::vector<std::string> reference_tokens;\n};\n}  // namespace nlohmann\n\n// #include <nlohmann/adl_serializer.hpp>\n\n\n#include <utility>\n\n// #include <nlohmann/detail/conversions/from_json.hpp>\n\n// #include <nlohmann/detail/conversions/to_json.hpp>\n\n\nnamespace nlohmann\n{\n\ntemplate<typename, typename>\nstruct adl_serializer\n{\n    /*!\n    @brief convert a JSON value to any value type\n\n    This function is usually called by the `get()` function of the\n    @ref basic_json class (either explicit or via conversion operators).\n\n    @param[in] j        JSON value to read from\n    @param[in,out] val  value to write to\n    */\n    template<typename BasicJsonType, typename ValueType>\n    static auto from_json(BasicJsonType&& j, ValueType& val) noexcept(\n        noexcept(::nlohmann::from_json(std::forward<BasicJsonType>(j), val)))\n    -> decltype(::nlohmann::from_json(std::forward<BasicJsonType>(j), val), void())\n    {\n        ::nlohmann::from_json(std::forward<BasicJsonType>(j), val);\n    }\n\n    /*!\n    @brief convert any value type to a JSON value\n\n    This function is usually called by the constructors of the @ref basic_json\n    class.\n\n    @param[in,out] j  JSON value to write to\n    @param[in] val    value to read from\n    */\n    template <typename BasicJsonType, typename ValueType>\n    static auto to_json(BasicJsonType& j, ValueType&& val) noexcept(\n        noexcept(::nlohmann::to_json(j, std::forward<ValueType>(val))))\n    -> decltype(::nlohmann::to_json(j, std::forward<ValueType>(val)), void())\n    {\n        ::nlohmann::to_json(j, std::forward<ValueType>(val));\n    }\n};\n\n}  // namespace nlohmann\n\n\n/*!\n@brief namespace for Niels Lohmann\n@see https://github.com/nlohmann\n@since version 1.0.0\n*/\nnamespace nlohmann\n{\n\n/*!\n@brief a class to store JSON values\n\n@tparam ObjectType type for JSON objects (`std::map` by default; will be used\nin @ref object_t)\n@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used\nin @ref array_t)\n@tparam StringType type for JSON strings and object keys (`std::string` by\ndefault; will be used in @ref string_t)\n@tparam BooleanType type for JSON booleans (`bool` by default; will be used\nin @ref boolean_t)\n@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by\ndefault; will be used in @ref number_integer_t)\n@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c\n`uint64_t` by default; will be used in @ref number_unsigned_t)\n@tparam NumberFloatType type for JSON floating-point numbers (`double` by\ndefault; will be used in @ref number_float_t)\n@tparam AllocatorType type of the allocator to use (`std::allocator` by\ndefault)\n@tparam JSONSerializer the serializer to resolve internal calls to `to_json()`\nand `from_json()` (@ref adl_serializer by default)\n\n@requirement The class satisfies the following concept requirements:\n- Basic\n - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible):\n   JSON values can be default constructed. The result will be a JSON null\n   value.\n - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible):\n   A JSON value can be constructed from an rvalue argument.\n - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible):\n   A JSON value can be copy-constructed from an lvalue expression.\n - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable):\n   A JSON value van be assigned from an rvalue argument.\n - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable):\n   A JSON value can be copy-assigned from an lvalue expression.\n - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible):\n   JSON values can be destructed.\n- Layout\n - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType):\n   JSON values have\n   [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout):\n   All non-static data members are private and standard layout types, the\n   class has no virtual functions or (virtual) base classes.\n- Library-wide\n - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable):\n   JSON values can be compared with `==`, see @ref\n   operator==(const_reference,const_reference).\n - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable):\n   JSON values can be compared with `<`, see @ref\n   operator<(const_reference,const_reference).\n - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable):\n   Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of\n   other compatible types, using unqualified function call @ref swap().\n - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer):\n   JSON values can be compared against `std::nullptr_t` objects which are used\n   to model the `null` value.\n- Container\n - [Container](https://en.cppreference.com/w/cpp/named_req/Container):\n   JSON values can be used like STL containers and provide iterator access.\n - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer);\n   JSON values can be used like STL containers and provide reverse iterator\n   access.\n\n@invariant The member variables @a m_value and @a m_type have the following\nrelationship:\n- If `m_type == value_t::object`, then `m_value.object != nullptr`.\n- If `m_type == value_t::array`, then `m_value.array != nullptr`.\n- If `m_type == value_t::string`, then `m_value.string != nullptr`.\nThe invariants are checked by member function assert_invariant().\n\n@internal\n@note ObjectType trick from http://stackoverflow.com/a/9860911\n@endinternal\n\n@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange\nFormat](http://rfc7159.net/rfc7159)\n\n@since version 1.0.0\n\n@nosubgrouping\n*/\nNLOHMANN_BASIC_JSON_TPL_DECLARATION\nclass basic_json\n{\n  private:\n    template<detail::value_t> friend struct detail::external_constructor;\n    friend ::nlohmann::json_pointer<basic_json>;\n    friend ::nlohmann::detail::parser<basic_json>;\n    friend ::nlohmann::detail::serializer<basic_json>;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::iter_impl;\n    template<typename BasicJsonType, typename CharType>\n    friend class ::nlohmann::detail::binary_writer;\n    template<typename BasicJsonType, typename SAX>\n    friend class ::nlohmann::detail::binary_reader;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::json_sax_dom_parser;\n    template<typename BasicJsonType>\n    friend class ::nlohmann::detail::json_sax_dom_callback_parser;\n\n    /// workaround type for MSVC\n    using basic_json_t = NLOHMANN_BASIC_JSON_TPL;\n\n    // convenience aliases for types residing in namespace detail;\n    using lexer = ::nlohmann::detail::lexer<basic_json>;\n    using parser = ::nlohmann::detail::parser<basic_json>;\n\n    using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t;\n    template<typename BasicJsonType>\n    using internal_iterator = ::nlohmann::detail::internal_iterator<BasicJsonType>;\n    template<typename BasicJsonType>\n    using iter_impl = ::nlohmann::detail::iter_impl<BasicJsonType>;\n    template<typename Iterator>\n    using iteration_proxy = ::nlohmann::detail::iteration_proxy<Iterator>;\n    template<typename Base> using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator<Base>;\n\n    template<typename CharType>\n    using output_adapter_t = ::nlohmann::detail::output_adapter_t<CharType>;\n\n    using binary_reader = ::nlohmann::detail::binary_reader<basic_json>;\n    template<typename CharType> using binary_writer = ::nlohmann::detail::binary_writer<basic_json, CharType>;\n\n    using serializer = ::nlohmann::detail::serializer<basic_json>;\n\n  public:\n    using value_t = detail::value_t;\n    /// JSON Pointer, see @ref nlohmann::json_pointer\n    using json_pointer = ::nlohmann::json_pointer<basic_json>;\n    template<typename T, typename SFINAE>\n    using json_serializer = JSONSerializer<T, SFINAE>;\n    /// how to treat decoding errors\n    using error_handler_t = detail::error_handler_t;\n    /// helper type for initializer lists of basic_json values\n    using initializer_list_t = std::initializer_list<detail::json_ref<basic_json>>;\n\n    using input_format_t = detail::input_format_t;\n    /// SAX interface type, see @ref nlohmann::json_sax\n    using json_sax_t = json_sax<basic_json>;\n\n    ////////////////\n    // exceptions //\n    ////////////////\n\n    /// @name exceptions\n    /// Classes to implement user-defined exceptions.\n    /// @{\n\n    /// @copydoc detail::exception\n    using exception = detail::exception;\n    /// @copydoc detail::parse_error\n    using parse_error = detail::parse_error;\n    /// @copydoc detail::invalid_iterator\n    using invalid_iterator = detail::invalid_iterator;\n    /// @copydoc detail::type_error\n    using type_error = detail::type_error;\n    /// @copydoc detail::out_of_range\n    using out_of_range = detail::out_of_range;\n    /// @copydoc detail::other_error\n    using other_error = detail::other_error;\n\n    /// @}\n\n\n    /////////////////////\n    // container types //\n    /////////////////////\n\n    /// @name container types\n    /// The canonic container types to use @ref basic_json like any other STL\n    /// container.\n    /// @{\n\n    /// the type of elements in a basic_json container\n    using value_type = basic_json;\n\n    /// the type of an element reference\n    using reference = value_type&;\n    /// the type of an element const reference\n    using const_reference = const value_type&;\n\n    /// a type to represent differences between iterators\n    using difference_type = std::ptrdiff_t;\n    /// a type to represent container sizes\n    using size_type = std::size_t;\n\n    /// the allocator type\n    using allocator_type = AllocatorType<basic_json>;\n\n    /// the type of an element pointer\n    using pointer = typename std::allocator_traits<allocator_type>::pointer;\n    /// the type of an element const pointer\n    using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;\n\n    /// an iterator for a basic_json container\n    using iterator = iter_impl<basic_json>;\n    /// a const iterator for a basic_json container\n    using const_iterator = iter_impl<const basic_json>;\n    /// a reverse iterator for a basic_json container\n    using reverse_iterator = json_reverse_iterator<typename basic_json::iterator>;\n    /// a const reverse iterator for a basic_json container\n    using const_reverse_iterator = json_reverse_iterator<typename basic_json::const_iterator>;\n\n    /// @}\n\n\n    /*!\n    @brief returns the allocator associated with the container\n    */\n    static allocator_type get_allocator()\n    {\n        return allocator_type();\n    }\n\n    /*!\n    @brief returns version information on the library\n\n    This function returns a JSON object with information about the library,\n    including the version number and information on the platform and compiler.\n\n    @return JSON object holding version information\n    key         | description\n    ----------- | ---------------\n    `compiler`  | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version).\n    `copyright` | The copyright line for the library as string.\n    `name`      | The name of the library as string.\n    `platform`  | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`.\n    `url`       | The URL of the project as string.\n    `version`   | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string).\n\n    @liveexample{The following code shows an example output of the `meta()`\n    function.,meta}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @complexity Constant.\n\n    @since 2.1.0\n    */\n    static basic_json meta()\n    {\n        basic_json result;\n\n        result[\"copyright\"] = \"(C) 2013-2017 Niels Lohmann\";\n        result[\"name\"] = \"JSON for Modern C++\";\n        result[\"url\"] = \"https://github.com/nlohmann/json\";\n        result[\"version\"][\"string\"] =\n            std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + \".\" +\n            std::to_string(NLOHMANN_JSON_VERSION_MINOR) + \".\" +\n            std::to_string(NLOHMANN_JSON_VERSION_PATCH);\n        result[\"version\"][\"major\"] = NLOHMANN_JSON_VERSION_MAJOR;\n        result[\"version\"][\"minor\"] = NLOHMANN_JSON_VERSION_MINOR;\n        result[\"version\"][\"patch\"] = NLOHMANN_JSON_VERSION_PATCH;\n\n#ifdef _WIN32\n        result[\"platform\"] = \"win32\";\n#elif defined __linux__\n        result[\"platform\"] = \"linux\";\n#elif defined __APPLE__\n        result[\"platform\"] = \"apple\";\n#elif defined __unix__\n        result[\"platform\"] = \"unix\";\n#else\n        result[\"platform\"] = \"unknown\";\n#endif\n\n#if defined(__ICC) || defined(__INTEL_COMPILER)\n        result[\"compiler\"] = {{\"family\", \"icc\"}, {\"version\", __INTEL_COMPILER}};\n#elif defined(__clang__)\n        result[\"compiler\"] = {{\"family\", \"clang\"}, {\"version\", __clang_version__}};\n#elif defined(__GNUC__) || defined(__GNUG__)\n        result[\"compiler\"] = {{\"family\", \"gcc\"}, {\"version\", std::to_string(__GNUC__) + \".\" + std::to_string(__GNUC_MINOR__) + \".\" + std::to_string(__GNUC_PATCHLEVEL__)}};\n#elif defined(__HP_cc) || defined(__HP_aCC)\n        result[\"compiler\"] = \"hp\"\n#elif defined(__IBMCPP__)\n        result[\"compiler\"] = {{\"family\", \"ilecpp\"}, {\"version\", __IBMCPP__}};\n#elif defined(_MSC_VER)\n        result[\"compiler\"] = {{\"family\", \"msvc\"}, {\"version\", _MSC_VER}};\n#elif defined(__PGI)\n        result[\"compiler\"] = {{\"family\", \"pgcpp\"}, {\"version\", __PGI}};\n#elif defined(__SUNPRO_CC)\n        result[\"compiler\"] = {{\"family\", \"sunpro\"}, {\"version\", __SUNPRO_CC}};\n#else\n        result[\"compiler\"] = {{\"family\", \"unknown\"}, {\"version\", \"unknown\"}};\n#endif\n\n#ifdef __cplusplus\n        result[\"compiler\"][\"c++\"] = std::to_string(__cplusplus);\n#else\n        result[\"compiler\"][\"c++\"] = \"unknown\";\n#endif\n        return result;\n    }\n\n\n    ///////////////////////////\n    // JSON value data types //\n    ///////////////////////////\n\n    /// @name JSON value data types\n    /// The data types to store a JSON value. These types are derived from\n    /// the template arguments passed to class @ref basic_json.\n    /// @{\n\n#if defined(JSON_HAS_CPP_14)\n    // Use transparent comparator if possible, combined with perfect forwarding\n    // on find() and count() calls prevents unnecessary string construction.\n    using object_comparator_t = std::less<>;\n#else\n    using object_comparator_t = std::less<StringType>;\n#endif\n\n    /*!\n    @brief a type for an object\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows:\n    > An object is an unordered collection of zero or more name/value pairs,\n    > where a name is a string and a value is a string, number, boolean, null,\n    > object, or array.\n\n    To store objects in C++, a type is defined by the template parameters\n    described below.\n\n    @tparam ObjectType  the container to store objects (e.g., `std::map` or\n    `std::unordered_map`)\n    @tparam StringType the type of the keys or names (e.g., `std::string`).\n    The comparison function `std::less<StringType>` is used to order elements\n    inside the container.\n    @tparam AllocatorType the allocator to use for objects (e.g.,\n    `std::allocator`)\n\n    #### Default type\n\n    With the default values for @a ObjectType (`std::map`), @a StringType\n    (`std::string`), and @a AllocatorType (`std::allocator`), the default\n    value for @a object_t is:\n\n    @code {.cpp}\n    std::map<\n      std::string, // key_type\n      basic_json, // value_type\n      std::less<std::string>, // key_compare\n      std::allocator<std::pair<const std::string, basic_json>> // allocator_type\n    >\n    @endcode\n\n    #### Behavior\n\n    The choice of @a object_t influences the behavior of the JSON class. With\n    the default type, objects have the following behavior:\n\n    - When all names are unique, objects will be interoperable in the sense\n      that all software implementations receiving that object will agree on\n      the name-value mappings.\n    - When the names within an object are not unique, it is unspecified which\n      one of the values for a given key will be chosen. For instance,\n      `{\"key\": 2, \"key\": 1}` could be equal to either `{\"key\": 1}` or\n      `{\"key\": 2}`.\n    - Internally, name/value pairs are stored in lexicographical order of the\n      names. Objects will also be serialized (see @ref dump) in this order.\n      For instance, `{\"b\": 1, \"a\": 2}` and `{\"a\": 2, \"b\": 1}` will be stored\n      and serialized as `{\"a\": 2, \"b\": 1}`.\n    - When comparing objects, the order of the name/value pairs is irrelevant.\n      This makes objects interoperable in the sense that they will not be\n      affected by these differences. For instance, `{\"b\": 1, \"a\": 2}` and\n      `{\"a\": 2, \"b\": 1}` will be treated as equal.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the maximum depth of nesting.\n\n    In this class, the object's limit of nesting is not explicitly constrained.\n    However, a maximum depth of nesting may be introduced by the compiler or\n    runtime environment. A theoretical limit can be queried by calling the\n    @ref max_size function of a JSON object.\n\n    #### Storage\n\n    Objects are stored as pointers in a @ref basic_json type. That is, for any\n    access to object values, a pointer of type `object_t*` must be\n    dereferenced.\n\n    @sa @ref array_t -- type for an array value\n\n    @since version 1.0.0\n\n    @note The order name/value pairs are added to the object is *not*\n    preserved by the library. Therefore, iterating an object may return\n    name/value pairs in a different order than they were originally stored. In\n    fact, keys will be traversed in alphabetical order as `std::map` with\n    `std::less` is used by default. Please note this behavior conforms to [RFC\n    7159](http://rfc7159.net/rfc7159), because any order implements the\n    specified \"unordered\" nature of JSON objects.\n    */\n    using object_t = ObjectType<StringType,\n          basic_json,\n          object_comparator_t,\n          AllocatorType<std::pair<const StringType,\n          basic_json>>>;\n\n    /*!\n    @brief a type for an array\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows:\n    > An array is an ordered sequence of zero or more values.\n\n    To store objects in C++, a type is defined by the template parameters\n    explained below.\n\n    @tparam ArrayType  container type to store arrays (e.g., `std::vector` or\n    `std::list`)\n    @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`)\n\n    #### Default type\n\n    With the default values for @a ArrayType (`std::vector`) and @a\n    AllocatorType (`std::allocator`), the default value for @a array_t is:\n\n    @code {.cpp}\n    std::vector<\n      basic_json, // value_type\n      std::allocator<basic_json> // allocator_type\n    >\n    @endcode\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the maximum depth of nesting.\n\n    In this class, the array's limit of nesting is not explicitly constrained.\n    However, a maximum depth of nesting may be introduced by the compiler or\n    runtime environment. A theoretical limit can be queried by calling the\n    @ref max_size function of a JSON array.\n\n    #### Storage\n\n    Arrays are stored as pointers in a @ref basic_json type. That is, for any\n    access to array values, a pointer of type `array_t*` must be dereferenced.\n\n    @sa @ref object_t -- type for an object value\n\n    @since version 1.0.0\n    */\n    using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;\n\n    /*!\n    @brief a type for a string\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows:\n    > A string is a sequence of zero or more Unicode characters.\n\n    To store objects in C++, a type is defined by the template parameter\n    described below. Unicode values are split by the JSON class into\n    byte-sized characters during deserialization.\n\n    @tparam StringType  the container to store strings (e.g., `std::string`).\n    Note this container is used for keys/names in objects, see @ref object_t.\n\n    #### Default type\n\n    With the default values for @a StringType (`std::string`), the default\n    value for @a string_t is:\n\n    @code {.cpp}\n    std::string\n    @endcode\n\n    #### Encoding\n\n    Strings are stored in UTF-8 encoding. Therefore, functions like\n    `std::string::size()` or `std::string::length()` return the number of\n    bytes in the string rather than the number of characters or glyphs.\n\n    #### String comparison\n\n    [RFC 7159](http://rfc7159.net/rfc7159) states:\n    > Software implementations are typically required to test names of object\n    > members for equality. Implementations that transform the textual\n    > representation into sequences of Unicode code units and then perform the\n    > comparison numerically, code unit by code unit, are interoperable in the\n    > sense that implementations will agree in all cases on equality or\n    > inequality of two strings. For example, implementations that compare\n    > strings with escaped characters unconverted may incorrectly find that\n    > `\"a\\\\b\"` and `\"a\\u005Cb\"` are not equal.\n\n    This implementation is interoperable as it does compare strings code unit\n    by code unit.\n\n    #### Storage\n\n    String values are stored as pointers in a @ref basic_json type. That is,\n    for any access to string values, a pointer of type `string_t*` must be\n    dereferenced.\n\n    @since version 1.0.0\n    */\n    using string_t = StringType;\n\n    /*!\n    @brief a type for a boolean\n\n    [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a\n    type which differentiates the two literals `true` and `false`.\n\n    To store objects in C++, a type is defined by the template parameter @a\n    BooleanType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a BooleanType (`bool`), the default value for\n    @a boolean_t is:\n\n    @code {.cpp}\n    bool\n    @endcode\n\n    #### Storage\n\n    Boolean values are stored directly inside a @ref basic_json type.\n\n    @since version 1.0.0\n    */\n    using boolean_t = BooleanType;\n\n    /*!\n    @brief a type for a number (integer)\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store integer numbers in C++, a type is defined by the template\n    parameter @a NumberIntegerType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberIntegerType (`int64_t`), the default\n    value for @a number_integer_t is:\n\n    @code {.cpp}\n    int64_t\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in integer literals lead to an interpretation as octal\n      number. Internally, the value will be stored as decimal number. For\n      instance, the C++ integer literal `010` will be serialized to `8`.\n      During deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the range and precision of numbers.\n\n    When the default type is used, the maximal integer number that can be\n    stored is `9223372036854775807` (INT64_MAX) and the minimal integer number\n    that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers\n    that are out of range will yield over/underflow when used in a\n    constructor. During deserialization, too large or small integer numbers\n    will be automatically be stored as @ref number_unsigned_t or @ref\n    number_float_t.\n\n    [RFC 7159](http://rfc7159.net/rfc7159) further states:\n    > Note that when such software is used, numbers that are integers and are\n    > in the range \\f$[-2^{53}+1, 2^{53}-1]\\f$ are interoperable in the sense\n    > that implementations will agree exactly on their numeric values.\n\n    As this range is a subrange of the exactly supported range [INT64_MIN,\n    INT64_MAX], this class's integer type is interoperable.\n\n    #### Storage\n\n    Integer number values are stored directly inside a @ref basic_json type.\n\n    @sa @ref number_float_t -- type for number values (floating-point)\n\n    @sa @ref number_unsigned_t -- type for number values (unsigned integer)\n\n    @since version 1.0.0\n    */\n    using number_integer_t = NumberIntegerType;\n\n    /*!\n    @brief a type for a number (unsigned)\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store unsigned integer numbers in C++, a type is defined by the\n    template parameter @a NumberUnsignedType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberUnsignedType (`uint64_t`), the\n    default value for @a number_unsigned_t is:\n\n    @code {.cpp}\n    uint64_t\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in integer literals lead to an interpretation as octal\n      number. Internally, the value will be stored as decimal number. For\n      instance, the C++ integer literal `010` will be serialized to `8`.\n      During deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) specifies:\n    > An implementation may set limits on the range and precision of numbers.\n\n    When the default type is used, the maximal integer number that can be\n    stored is `18446744073709551615` (UINT64_MAX) and the minimal integer\n    number that can be stored is `0`. Integer numbers that are out of range\n    will yield over/underflow when used in a constructor. During\n    deserialization, too large or small integer numbers will be automatically\n    be stored as @ref number_integer_t or @ref number_float_t.\n\n    [RFC 7159](http://rfc7159.net/rfc7159) further states:\n    > Note that when such software is used, numbers that are integers and are\n    > in the range \\f$[-2^{53}+1, 2^{53}-1]\\f$ are interoperable in the sense\n    > that implementations will agree exactly on their numeric values.\n\n    As this range is a subrange (when considered in conjunction with the\n    number_integer_t type) of the exactly supported range [0, UINT64_MAX],\n    this class's integer type is interoperable.\n\n    #### Storage\n\n    Integer number values are stored directly inside a @ref basic_json type.\n\n    @sa @ref number_float_t -- type for number values (floating-point)\n    @sa @ref number_integer_t -- type for number values (integer)\n\n    @since version 2.0.0\n    */\n    using number_unsigned_t = NumberUnsignedType;\n\n    /*!\n    @brief a type for a number (floating-point)\n\n    [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:\n    > The representation of numbers is similar to that used in most\n    > programming languages. A number is represented in base 10 using decimal\n    > digits. It contains an integer component that may be prefixed with an\n    > optional minus sign, which may be followed by a fraction part and/or an\n    > exponent part. Leading zeros are not allowed. (...) Numeric values that\n    > cannot be represented in the grammar below (such as Infinity and NaN)\n    > are not permitted.\n\n    This description includes both integer and floating-point numbers.\n    However, C++ allows more precise storage if it is known whether the number\n    is a signed integer, an unsigned integer or a floating-point number.\n    Therefore, three different types, @ref number_integer_t, @ref\n    number_unsigned_t and @ref number_float_t are used.\n\n    To store floating-point numbers in C++, a type is defined by the template\n    parameter @a NumberFloatType which chooses the type to use.\n\n    #### Default type\n\n    With the default values for @a NumberFloatType (`double`), the default\n    value for @a number_float_t is:\n\n    @code {.cpp}\n    double\n    @endcode\n\n    #### Default behavior\n\n    - The restrictions about leading zeros is not enforced in C++. Instead,\n      leading zeros in floating-point literals will be ignored. Internally,\n      the value will be stored as decimal number. For instance, the C++\n      floating-point literal `01.2` will be serialized to `1.2`. During\n      deserialization, leading zeros yield an error.\n    - Not-a-number (NaN) values will be serialized to `null`.\n\n    #### Limits\n\n    [RFC 7159](http://rfc7159.net/rfc7159) states:\n    > This specification allows implementations to set limits on the range and\n    > precision of numbers accepted. Since software that implements IEEE\n    > 754-2008 binary64 (double precision) numbers is generally available and\n    > widely used, good interoperability can be achieved by implementations\n    > that expect no more precision or range than these provide, in the sense\n    > that implementations will approximate JSON numbers within the expected\n    > precision.\n\n    This implementation does exactly follow this approach, as it uses double\n    precision floating-point numbers. Note values smaller than\n    `-1.79769313486232e+308` and values greater than `1.79769313486232e+308`\n    will be stored as NaN internally and be serialized to `null`.\n\n    #### Storage\n\n    Floating-point number values are stored directly inside a @ref basic_json\n    type.\n\n    @sa @ref number_integer_t -- type for number values (integer)\n\n    @sa @ref number_unsigned_t -- type for number values (unsigned integer)\n\n    @since version 1.0.0\n    */\n    using number_float_t = NumberFloatType;\n\n    /// @}\n\n  private:\n\n    /// helper for exception-safe object creation\n    template<typename T, typename... Args>\n    static T* create(Args&& ... args)\n    {\n        AllocatorType<T> alloc;\n        using AllocatorTraits = std::allocator_traits<AllocatorType<T>>;\n\n        auto deleter = [&](T * object)\n        {\n            AllocatorTraits::deallocate(alloc, object, 1);\n        };\n        std::unique_ptr<T, decltype(deleter)> object(AllocatorTraits::allocate(alloc, 1), deleter);\n        AllocatorTraits::construct(alloc, object.get(), std::forward<Args>(args)...);\n        assert(object != nullptr);\n        return object.release();\n    }\n\n    ////////////////////////\n    // JSON value storage //\n    ////////////////////////\n\n    /*!\n    @brief a JSON value\n\n    The actual storage for a JSON value of the @ref basic_json class. This\n    union combines the different storage types for the JSON value types\n    defined in @ref value_t.\n\n    JSON type | value_t type    | used type\n    --------- | --------------- | ------------------------\n    object    | object          | pointer to @ref object_t\n    array     | array           | pointer to @ref array_t\n    string    | string          | pointer to @ref string_t\n    boolean   | boolean         | @ref boolean_t\n    number    | number_integer  | @ref number_integer_t\n    number    | number_unsigned | @ref number_unsigned_t\n    number    | number_float    | @ref number_float_t\n    null      | null            | *no value is stored*\n\n    @note Variable-length types (objects, arrays, and strings) are stored as\n    pointers. The size of the union should not exceed 64 bits if the default\n    value types are used.\n\n    @since version 1.0.0\n    */\n    union json_value\n    {\n        /// object (stored with pointer to save storage)\n        object_t* object;\n        /// array (stored with pointer to save storage)\n        array_t* array;\n        /// string (stored with pointer to save storage)\n        string_t* string;\n        /// boolean\n        boolean_t boolean;\n        /// number (integer)\n        number_integer_t number_integer;\n        /// number (unsigned integer)\n        number_unsigned_t number_unsigned;\n        /// number (floating-point)\n        number_float_t number_float;\n\n        /// default constructor (for null values)\n        json_value() = default;\n        /// constructor for booleans\n        json_value(boolean_t v) noexcept : boolean(v) {}\n        /// constructor for numbers (integer)\n        json_value(number_integer_t v) noexcept : number_integer(v) {}\n        /// constructor for numbers (unsigned)\n        json_value(number_unsigned_t v) noexcept : number_unsigned(v) {}\n        /// constructor for numbers (floating-point)\n        json_value(number_float_t v) noexcept : number_float(v) {}\n        /// constructor for empty values of a given type\n        json_value(value_t t)\n        {\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    object = create<object_t>();\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    array = create<array_t>();\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    string = create<string_t>(\"\");\n                    break;\n                }\n\n                case value_t::boolean:\n                {\n                    boolean = boolean_t(false);\n                    break;\n                }\n\n                case value_t::number_integer:\n                {\n                    number_integer = number_integer_t(0);\n                    break;\n                }\n\n                case value_t::number_unsigned:\n                {\n                    number_unsigned = number_unsigned_t(0);\n                    break;\n                }\n\n                case value_t::number_float:\n                {\n                    number_float = number_float_t(0.0);\n                    break;\n                }\n\n                case value_t::null:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    break;\n                }\n\n                default:\n                {\n                    object = nullptr;  // silence warning, see #821\n                    if (JSON_UNLIKELY(t == value_t::null))\n                    {\n                        JSON_THROW(other_error::create(500, \"961c151d2e87f2686a955a9be24d316f1362bf21 3.4.0\")); // LCOV_EXCL_LINE\n                    }\n                    break;\n                }\n            }\n        }\n\n        /// constructor for strings\n        json_value(const string_t& value)\n        {\n            string = create<string_t>(value);\n        }\n\n        /// constructor for rvalue strings\n        json_value(string_t&& value)\n        {\n            string = create<string_t>(std::move(value));\n        }\n\n        /// constructor for objects\n        json_value(const object_t& value)\n        {\n            object = create<object_t>(value);\n        }\n\n        /// constructor for rvalue objects\n        json_value(object_t&& value)\n        {\n            object = create<object_t>(std::move(value));\n        }\n\n        /// constructor for arrays\n        json_value(const array_t& value)\n        {\n            array = create<array_t>(value);\n        }\n\n        /// constructor for rvalue arrays\n        json_value(array_t&& value)\n        {\n            array = create<array_t>(std::move(value));\n        }\n\n        void destroy(value_t t) noexcept\n        {\n            switch (t)\n            {\n                case value_t::object:\n                {\n                    AllocatorType<object_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, object);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, object, 1);\n                    break;\n                }\n\n                case value_t::array:\n                {\n                    AllocatorType<array_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, array);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, array, 1);\n                    break;\n                }\n\n                case value_t::string:\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, string, 1);\n                    break;\n                }\n\n                default:\n                {\n                    break;\n                }\n            }\n        }\n    };\n\n    /*!\n    @brief checks the class invariants\n\n    This function asserts the class invariants. It needs to be called at the\n    end of every constructor to make sure that created objects respect the\n    invariant. Furthermore, it has to be called each time the type of a JSON\n    value is changed, because the invariant expresses a relationship between\n    @a m_type and @a m_value.\n    */\n    void assert_invariant() const noexcept\n    {\n        assert(m_type != value_t::object or m_value.object != nullptr);\n        assert(m_type != value_t::array or m_value.array != nullptr);\n        assert(m_type != value_t::string or m_value.string != nullptr);\n    }\n\n  public:\n    //////////////////////////\n    // JSON parser callback //\n    //////////////////////////\n\n    /*!\n    @brief parser event types\n\n    The parser callback distinguishes the following events:\n    - `object_start`: the parser read `{` and started to process a JSON object\n    - `key`: the parser read a key of a value in an object\n    - `object_end`: the parser read `}` and finished processing a JSON object\n    - `array_start`: the parser read `[` and started to process a JSON array\n    - `array_end`: the parser read `]` and finished processing a JSON array\n    - `value`: the parser finished reading a JSON value\n\n    @image html callback_events.png \"Example when certain parse events are triggered\"\n\n    @sa @ref parser_callback_t for more information and examples\n    */\n    using parse_event_t = typename parser::parse_event_t;\n\n    /*!\n    @brief per-element parser callback type\n\n    With a parser callback function, the result of parsing a JSON text can be\n    influenced. When passed to @ref parse, it is called on certain events\n    (passed as @ref parse_event_t via parameter @a event) with a set recursion\n    depth @a depth and context JSON value @a parsed. The return value of the\n    callback function is a boolean indicating whether the element that emitted\n    the callback shall be kept or not.\n\n    We distinguish six scenarios (determined by the event type) in which the\n    callback function can be called. The following table describes the values\n    of the parameters @a depth, @a event, and @a parsed.\n\n    parameter @a event | description | parameter @a depth | parameter @a parsed\n    ------------------ | ----------- | ------------------ | -------------------\n    parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded\n    parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key\n    parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object\n    parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded\n    parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array\n    parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value\n\n    @image html callback_events.png \"Example when certain parse events are triggered\"\n\n    Discarding a value (i.e., returning `false`) has different effects\n    depending on the context in which function was called:\n\n    - Discarded values in structured types are skipped. That is, the parser\n      will behave as if the discarded value was never read.\n    - In case a value outside a structured type is skipped, it is replaced\n      with `null`. This case happens if the top-level element is skipped.\n\n    @param[in] depth  the depth of the recursion during parsing\n\n    @param[in] event  an event of type parse_event_t indicating the context in\n    the callback function has been called\n\n    @param[in,out] parsed  the current intermediate parse result; note that\n    writing to this value has no effect for parse_event_t::key events\n\n    @return Whether the JSON value which called the function during parsing\n    should be kept (`true`) or not (`false`). In the latter case, it is either\n    skipped completely or replaced by an empty discarded object.\n\n    @sa @ref parse for examples\n\n    @since version 1.0.0\n    */\n    using parser_callback_t = typename parser::parser_callback_t;\n\n    //////////////////\n    // constructors //\n    //////////////////\n\n    /// @name constructors and destructors\n    /// Constructors of class @ref basic_json, copy/move constructor, copy\n    /// assignment, static functions creating objects, and the destructor.\n    /// @{\n\n    /*!\n    @brief create an empty value with a given type\n\n    Create an empty JSON value with a given type. The value will be default\n    initialized with an empty value which depends on the type:\n\n    Value type  | initial value\n    ----------- | -------------\n    null        | `null`\n    boolean     | `false`\n    string      | `\"\"`\n    number      | `0`\n    object      | `{}`\n    array       | `[]`\n\n    @param[in] v  the type of the value to create\n\n    @complexity Constant.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows the constructor for different @ref\n    value_t values,basic_json__value_t}\n\n    @sa @ref clear() -- restores the postcondition of this constructor\n\n    @since version 1.0.0\n    */\n    basic_json(const value_t v)\n        : m_type(v), m_value(v)\n    {\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a null object\n\n    Create a `null` JSON value. It either takes a null pointer as parameter\n    (explicitly creating `null`) or no parameter (implicitly creating `null`).\n    The passed null pointer itself is not read -- it is only used to choose\n    the right constructor.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this constructor never throws\n    exceptions.\n\n    @liveexample{The following code shows the constructor with and without a\n    null pointer parameter.,basic_json__nullptr_t}\n\n    @since version 1.0.0\n    */\n    basic_json(std::nullptr_t = nullptr) noexcept\n        : basic_json(value_t::null)\n    {\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a JSON value\n\n    This is a \"catch all\" constructor for all compatible JSON types; that is,\n    types for which a `to_json()` method exists. The constructor forwards the\n    parameter @a val to that method (to `json_serializer<U>::to_json` method\n    with `U = uncvref_t<CompatibleType>`, to be exact).\n\n    Template type @a CompatibleType includes, but is not limited to, the\n    following types:\n    - **arrays**: @ref array_t and all kinds of compatible containers such as\n      `std::vector`, `std::deque`, `std::list`, `std::forward_list`,\n      `std::array`, `std::valarray`, `std::set`, `std::unordered_set`,\n      `std::multiset`, and `std::unordered_multiset` with a `value_type` from\n      which a @ref basic_json value can be constructed.\n    - **objects**: @ref object_t and all kinds of compatible associative\n      containers such as `std::map`, `std::unordered_map`, `std::multimap`,\n      and `std::unordered_multimap` with a `key_type` compatible to\n      @ref string_t and a `value_type` from which a @ref basic_json value can\n      be constructed.\n    - **strings**: @ref string_t, string literals, and all compatible string\n      containers can be used.\n    - **numbers**: @ref number_integer_t, @ref number_unsigned_t,\n      @ref number_float_t, and all convertible number types such as `int`,\n      `size_t`, `int64_t`, `float` or `double` can be used.\n    - **boolean**: @ref boolean_t / `bool` can be used.\n\n    See the examples below.\n\n    @tparam CompatibleType a type such that:\n    - @a CompatibleType is not derived from `std::istream`,\n    - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move\n         constructors),\n    - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments)\n    - @a CompatibleType is not a @ref basic_json nested type (e.g.,\n         @ref json_pointer, @ref iterator, etc ...)\n    - @ref @ref json_serializer<U> has a\n         `to_json(basic_json_t&, CompatibleType&&)` method\n\n    @tparam U = `uncvref_t<CompatibleType>`\n\n    @param[in] val the value to be forwarded to the respective constructor\n\n    @complexity Usually linear in the size of the passed @a val, also\n                depending on the implementation of the called `to_json()`\n                method.\n\n    @exceptionsafety Depends on the called constructor. For types directly\n    supported by the library (i.e., all types for which no `to_json()` function\n    was provided), strong guarantee holds: if an exception is thrown, there are\n    no changes to any JSON value.\n\n    @liveexample{The following code shows the constructor with several\n    compatible types.,basic_json__CompatibleType}\n\n    @since version 2.1.0\n    */\n    template <typename CompatibleType,\n              typename U = detail::uncvref_t<CompatibleType>,\n              detail::enable_if_t<\n                  not detail::is_basic_json<U>::value and detail::is_compatible_type<basic_json_t, U>::value, int> = 0>\n    basic_json(CompatibleType && val) noexcept(noexcept(\n                JSONSerializer<U>::to_json(std::declval<basic_json_t&>(),\n                                           std::forward<CompatibleType>(val))))\n    {\n        JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a JSON value from an existing one\n\n    This is a constructor for existing @ref basic_json types.\n    It does not hijack copy/move constructors, since the parameter has different\n    template arguments than the current ones.\n\n    The constructor tries to convert the internal @ref m_value of the parameter.\n\n    @tparam BasicJsonType a type such that:\n    - @a BasicJsonType is a @ref basic_json type.\n    - @a BasicJsonType has different template arguments than @ref basic_json_t.\n\n    @param[in] val the @ref basic_json value to be converted.\n\n    @complexity Usually linear in the size of the passed @a val, also\n                depending on the implementation of the called `to_json()`\n                method.\n\n    @exceptionsafety Depends on the called constructor. For types directly\n    supported by the library (i.e., all types for which no `to_json()` function\n    was provided), strong guarantee holds: if an exception is thrown, there are\n    no changes to any JSON value.\n\n    @since version 3.2.0\n    */\n    template <typename BasicJsonType,\n              detail::enable_if_t<\n                  detail::is_basic_json<BasicJsonType>::value and not std::is_same<basic_json, BasicJsonType>::value, int> = 0>\n    basic_json(const BasicJsonType& val)\n    {\n        using other_boolean_t = typename BasicJsonType::boolean_t;\n        using other_number_float_t = typename BasicJsonType::number_float_t;\n        using other_number_integer_t = typename BasicJsonType::number_integer_t;\n        using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;\n        using other_string_t = typename BasicJsonType::string_t;\n        using other_object_t = typename BasicJsonType::object_t;\n        using other_array_t = typename BasicJsonType::array_t;\n\n        switch (val.type())\n        {\n            case value_t::boolean:\n                JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());\n                break;\n            case value_t::number_float:\n                JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());\n                break;\n            case value_t::number_integer:\n                JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());\n                break;\n            case value_t::number_unsigned:\n                JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());\n                break;\n            case value_t::string:\n                JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());\n                break;\n            case value_t::object:\n                JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());\n                break;\n            case value_t::array:\n                JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());\n                break;\n            case value_t::null:\n                *this = nullptr;\n                break;\n            case value_t::discarded:\n                m_type = value_t::discarded;\n                break;\n        }\n        assert_invariant();\n    }\n\n    /*!\n    @brief create a container (array or object) from an initializer list\n\n    Creates a JSON value of type array or object from the passed initializer\n    list @a init. In case @a type_deduction is `true` (default), the type of\n    the JSON value to be created is deducted from the initializer list @a init\n    according to the following rules:\n\n    1. If the list is empty, an empty JSON object value `{}` is created.\n    2. If the list consists of pairs whose first element is a string, a JSON\n       object value is created where the first elements of the pairs are\n       treated as keys and the second elements are as values.\n    3. In all other cases, an array is created.\n\n    The rules aim to create the best fit between a C++ initializer list and\n    JSON values. The rationale is as follows:\n\n    1. The empty initializer list is written as `{}` which is exactly an empty\n       JSON object.\n    2. C++ has no way of describing mapped types other than to list a list of\n       pairs. As JSON requires that keys must be of type string, rule 2 is the\n       weakest constraint one can pose on initializer lists to interpret them\n       as an object.\n    3. In all other cases, the initializer list could not be interpreted as\n       JSON object type, so interpreting it as JSON array type is safe.\n\n    With the rules described above, the following JSON values cannot be\n    expressed by an initializer list:\n\n    - the empty array (`[]`): use @ref array(initializer_list_t)\n      with an empty initializer list in this case\n    - arrays whose elements satisfy rule 2: use @ref\n      array(initializer_list_t) with the same initializer list\n      in this case\n\n    @note When used without parentheses around an empty initializer list, @ref\n    basic_json() is called instead of this function, yielding the JSON null\n    value.\n\n    @param[in] init  initializer list with JSON values\n\n    @param[in] type_deduction internal parameter; when set to `true`, the type\n    of the JSON value is deducted from the initializer list @a init; when set\n    to `false`, the type provided via @a manual_type is forced. This mode is\n    used by the functions @ref array(initializer_list_t) and\n    @ref object(initializer_list_t).\n\n    @param[in] manual_type internal parameter; when @a type_deduction is set\n    to `false`, the created JSON value will use the provided type (only @ref\n    value_t::array and @ref value_t::object are valid); when @a type_deduction\n    is set to `true`, this parameter has no effect\n\n    @throw type_error.301 if @a type_deduction is `false`, @a manual_type is\n    `value_t::object`, but @a init contains an element which is not a pair\n    whose first element is a string. In this case, the constructor could not\n    create an object. If @a type_deduction would have be `true`, an array\n    would have been created. See @ref object(initializer_list_t)\n    for an example.\n\n    @complexity Linear in the size of the initializer list @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The example below shows how JSON values are created from\n    initializer lists.,basic_json__list_init_t}\n\n    @sa @ref array(initializer_list_t) -- create a JSON array\n    value from an initializer list\n    @sa @ref object(initializer_list_t) -- create a JSON object\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    basic_json(initializer_list_t init,\n               bool type_deduction = true,\n               value_t manual_type = value_t::array)\n    {\n        // check if each element is an array with two elements whose first\n        // element is a string\n        bool is_an_object = std::all_of(init.begin(), init.end(),\n                                        [](const detail::json_ref<basic_json>& element_ref)\n        {\n            return (element_ref->is_array() and element_ref->size() == 2 and (*element_ref)[0].is_string());\n        });\n\n        // adjust type if type deduction is not wanted\n        if (not type_deduction)\n        {\n            // if array is wanted, do not create an object though possible\n            if (manual_type == value_t::array)\n            {\n                is_an_object = false;\n            }\n\n            // if object is wanted but impossible, throw an exception\n            if (JSON_UNLIKELY(manual_type == value_t::object and not is_an_object))\n            {\n                JSON_THROW(type_error::create(301, \"cannot create object from initializer list\"));\n            }\n        }\n\n        if (is_an_object)\n        {\n            // the initializer list is a list of pairs -> create object\n            m_type = value_t::object;\n            m_value = value_t::object;\n\n            std::for_each(init.begin(), init.end(), [this](const detail::json_ref<basic_json>& element_ref)\n            {\n                auto element = element_ref.moved_or_copied();\n                m_value.object->emplace(\n                    std::move(*((*element.m_value.array)[0].m_value.string)),\n                    std::move((*element.m_value.array)[1]));\n            });\n        }\n        else\n        {\n            // the initializer list describes an array -> create array\n            m_type = value_t::array;\n            m_value.array = create<array_t>(init.begin(), init.end());\n        }\n\n        assert_invariant();\n    }\n\n    /*!\n    @brief explicitly create an array from an initializer list\n\n    Creates a JSON array value from a given initializer list. That is, given a\n    list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the\n    initializer list is empty, the empty array `[]` is created.\n\n    @note This function is only needed to express two edge cases that cannot\n    be realized with the initializer list constructor (@ref\n    basic_json(initializer_list_t, bool, value_t)). These cases\n    are:\n    1. creating an array whose elements are all pairs whose first element is a\n    string -- in this case, the initializer list constructor would create an\n    object, taking the first elements as keys\n    2. creating an empty array -- passing the empty initializer list to the\n    initializer list constructor yields an empty object\n\n    @param[in] init  initializer list with JSON values to create an array from\n    (optional)\n\n    @return JSON array value\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows an example for the `array`\n    function.,array}\n\n    @sa @ref basic_json(initializer_list_t, bool, value_t) --\n    create a JSON value from an initializer list\n    @sa @ref object(initializer_list_t) -- create a JSON object\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    static basic_json array(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::array);\n    }\n\n    /*!\n    @brief explicitly create an object from an initializer list\n\n    Creates a JSON object value from a given initializer list. The initializer\n    lists elements must be pairs, and their first elements must be strings. If\n    the initializer list is empty, the empty object `{}` is created.\n\n    @note This function is only added for symmetry reasons. In contrast to the\n    related function @ref array(initializer_list_t), there are\n    no cases which can only be expressed by this function. That is, any\n    initializer list @a init can also be passed to the initializer list\n    constructor @ref basic_json(initializer_list_t, bool, value_t).\n\n    @param[in] init  initializer list to create an object from (optional)\n\n    @return JSON object value\n\n    @throw type_error.301 if @a init is not a list of pairs whose first\n    elements are strings. In this case, no object can be created. When such a\n    value is passed to @ref basic_json(initializer_list_t, bool, value_t),\n    an array would have been created from the passed initializer list @a init.\n    See example below.\n\n    @complexity Linear in the size of @a init.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows an example for the `object`\n    function.,object}\n\n    @sa @ref basic_json(initializer_list_t, bool, value_t) --\n    create a JSON value from an initializer list\n    @sa @ref array(initializer_list_t) -- create a JSON array\n    value from an initializer list\n\n    @since version 1.0.0\n    */\n    static basic_json object(initializer_list_t init = {})\n    {\n        return basic_json(init, false, value_t::object);\n    }\n\n    /*!\n    @brief construct an array with count copies of given value\n\n    Constructs a JSON array value by creating @a cnt copies of a passed value.\n    In case @a cnt is `0`, an empty array is created.\n\n    @param[in] cnt  the number of JSON copies of @a val to create\n    @param[in] val  the JSON value to copy\n\n    @post `std::distance(begin(),end()) == cnt` holds.\n\n    @complexity Linear in @a cnt.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The following code shows examples for the @ref\n    basic_json(size_type\\, const basic_json&)\n    constructor.,basic_json__size_type_basic_json}\n\n    @since version 1.0.0\n    */\n    basic_json(size_type cnt, const basic_json& val)\n        : m_type(value_t::array)\n    {\n        m_value.array = create<array_t>(cnt, val);\n        assert_invariant();\n    }\n\n    /*!\n    @brief construct a JSON container given an iterator range\n\n    Constructs the JSON value with the contents of the range `[first, last)`.\n    The semantics depends on the different types a JSON value can have:\n    - In case of a null type, invalid_iterator.206 is thrown.\n    - In case of other primitive types (number, boolean, or string), @a first\n      must be `begin()` and @a last must be `end()`. In this case, the value is\n      copied. Otherwise, invalid_iterator.204 is thrown.\n    - In case of structured types (array, object), the constructor behaves as\n      similar versions for `std::vector` or `std::map`; that is, a JSON array\n      or object is constructed from the values in the range.\n\n    @tparam InputIT an input iterator type (@ref iterator or @ref\n    const_iterator)\n\n    @param[in] first begin of the range to copy from (included)\n    @param[in] last end of the range to copy from (excluded)\n\n    @pre Iterators @a first and @a last must be initialized. **This\n         precondition is enforced with an assertion (see warning).** If\n         assertions are switched off, a violation of this precondition yields\n         undefined behavior.\n\n    @pre Range `[first, last)` is valid. Usually, this precondition cannot be\n         checked efficiently. Only certain edge cases are detected; see the\n         description of the exceptions below. A violation of this precondition\n         yields undefined behavior.\n\n    @warning A precondition is enforced with a runtime assertion that will\n             result in calling `std::abort` if this precondition is not met.\n             Assertions can be disabled by defining `NDEBUG` at compile time.\n             See https://en.cppreference.com/w/cpp/error/assert for more\n             information.\n\n    @throw invalid_iterator.201 if iterators @a first and @a last are not\n    compatible (i.e., do not belong to the same JSON value). In this case,\n    the range `[first, last)` is undefined.\n    @throw invalid_iterator.204 if iterators @a first and @a last belong to a\n    primitive type (number, boolean, or string), but @a first does not point\n    to the first element any more. In this case, the range `[first, last)` is\n    undefined. See example code below.\n    @throw invalid_iterator.206 if iterators @a first and @a last belong to a\n    null value. In this case, the range `[first, last)` is undefined.\n\n    @complexity Linear in distance between @a first and @a last.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @liveexample{The example below shows several ways to create JSON values by\n    specifying a subrange with iterators.,basic_json__InputIt_InputIt}\n\n    @since version 1.0.0\n    */\n    template<class InputIT, typename std::enable_if<\n                 std::is_same<InputIT, typename basic_json_t::iterator>::value or\n                 std::is_same<InputIT, typename basic_json_t::const_iterator>::value, int>::type = 0>\n    basic_json(InputIT first, InputIT last)\n    {\n        assert(first.m_object != nullptr);\n        assert(last.m_object != nullptr);\n\n        // make sure iterator fits the current value\n        if (JSON_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(201, \"iterators are not compatible\"));\n        }\n\n        // copy type from first iterator\n        m_type = first.m_object->m_type;\n\n        // check if iterator range is complete for primitive values\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            {\n                if (JSON_UNLIKELY(not first.m_it.primitive_iterator.is_begin()\n                                  or not last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\"));\n                }\n                break;\n            }\n\n            default:\n                break;\n        }\n\n        switch (m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_value.number_integer = first.m_object->m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value.number_unsigned = first.m_object->m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value.number_float = first.m_object->m_value.number_float;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value.boolean = first.m_object->m_value.boolean;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value = *first.m_object->m_value.string;\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_value.object = create<object_t>(first.m_it.object_iterator,\n                                                  last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value.array = create<array_t>(first.m_it.array_iterator,\n                                                last.m_it.array_iterator);\n                break;\n            }\n\n            default:\n                JSON_THROW(invalid_iterator::create(206, \"cannot construct with iterators from \" +\n                                                    std::string(first.m_object->type_name())));\n        }\n\n        assert_invariant();\n    }\n\n\n    ///////////////////////////////////////\n    // other constructors and destructor //\n    ///////////////////////////////////////\n\n    /// @private\n    basic_json(const detail::json_ref<basic_json>& ref)\n        : basic_json(ref.moved_or_copied())\n    {}\n\n    /*!\n    @brief copy constructor\n\n    Creates a copy of a given JSON value.\n\n    @param[in] other  the JSON value to copy\n\n    @post `*this == other`\n\n    @complexity Linear in the size of @a other.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes to any JSON value.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n    - As postcondition, it holds: `other == basic_json(other)`.\n\n    @liveexample{The following code shows an example for the copy\n    constructor.,basic_json__basic_json}\n\n    @since version 1.0.0\n    */\n    basic_json(const basic_json& other)\n        : m_type(other.m_type)\n    {\n        // check of passed value is valid\n        other.assert_invariant();\n\n        switch (m_type)\n        {\n            case value_t::object:\n            {\n                m_value = *other.m_value.object;\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value = *other.m_value.array;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value = *other.m_value.string;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value = other.m_value.boolean;\n                break;\n            }\n\n            case value_t::number_integer:\n            {\n                m_value = other.m_value.number_integer;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value = other.m_value.number_unsigned;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value = other.m_value.number_float;\n                break;\n            }\n\n            default:\n                break;\n        }\n\n        assert_invariant();\n    }\n\n    /*!\n    @brief move constructor\n\n    Move constructor. Constructs a JSON value with the contents of the given\n    value @a other using move semantics. It \"steals\" the resources from @a\n    other and leaves it as JSON null value.\n\n    @param[in,out] other  value to move to this object\n\n    @post `*this` has the same value as @a other before the call.\n    @post @a other is a JSON null value.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this constructor never throws\n    exceptions.\n\n    @requirement This function helps `basic_json` satisfying the\n    [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible)\n    requirements.\n\n    @liveexample{The code below shows the move constructor explicitly called\n    via std::move.,basic_json__moveconstructor}\n\n    @since version 1.0.0\n    */\n    basic_json(basic_json&& other) noexcept\n        : m_type(std::move(other.m_type)),\n          m_value(std::move(other.m_value))\n    {\n        // check that passed value is valid\n        other.assert_invariant();\n\n        // invalidate payload\n        other.m_type = value_t::null;\n        other.m_value = {};\n\n        assert_invariant();\n    }\n\n    /*!\n    @brief copy assignment\n\n    Copy assignment operator. Copies a JSON value via the \"copy and swap\"\n    strategy: It is expressed in terms of the copy constructor, destructor,\n    and the `swap()` member function.\n\n    @param[in] other  value to copy from\n\n    @complexity Linear.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n\n    @liveexample{The code below shows and example for the copy assignment. It\n    creates a copy of value `a` which is then swapped with `b`. Finally\\, the\n    copy of `a` (which is the null value after the swap) is\n    destroyed.,basic_json__copyassignment}\n\n    @since version 1.0.0\n    */\n    basic_json& operator=(basic_json other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value and\n        std::is_nothrow_move_assignable<value_t>::value and\n        std::is_nothrow_move_constructible<json_value>::value and\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        // check that passed value is valid\n        other.assert_invariant();\n\n        using std::swap;\n        swap(m_type, other.m_type);\n        swap(m_value, other.m_value);\n\n        assert_invariant();\n        return *this;\n    }\n\n    /*!\n    @brief destructor\n\n    Destroys the JSON value and frees all allocated memory.\n\n    @complexity Linear.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is linear.\n    - All stored elements are destroyed and all memory is freed.\n\n    @since version 1.0.0\n    */\n    ~basic_json() noexcept\n    {\n        assert_invariant();\n        m_value.destroy(m_type);\n    }\n\n    /// @}\n\n  public:\n    ///////////////////////\n    // object inspection //\n    ///////////////////////\n\n    /// @name object inspection\n    /// Functions to inspect the type of a JSON value.\n    /// @{\n\n    /*!\n    @brief serialization\n\n    Serialization function for JSON values. The function tries to mimic\n    Python's `json.dumps()` function, and currently supports its @a indent\n    and @a ensure_ascii parameters.\n\n    @param[in] indent If indent is nonnegative, then array elements and object\n    members will be pretty-printed with that indent level. An indent level of\n    `0` will only insert newlines. `-1` (the default) selects the most compact\n    representation.\n    @param[in] indent_char The character to use for indentation if @a indent is\n    greater than `0`. The default is ` ` (space).\n    @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters\n    in the output are escaped with `\\uXXXX` sequences, and the result consists\n    of ASCII characters only.\n    @param[in] error_handler  how to react on decoding errors; there are three\n    possible values: `strict` (throws and exception in case a decoding error\n    occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD),\n    and `ignore` (ignore invalid UTF-8 sequences during serialization).\n\n    @return string containing the serialization of the JSON value\n\n    @throw type_error.316 if a string stored inside the JSON value is not\n                          UTF-8 encoded\n\n    @complexity Linear.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @liveexample{The following example shows the effect of different @a indent\\,\n    @a indent_char\\, and @a ensure_ascii parameters to the result of the\n    serialization.,dump}\n\n    @see https://docs.python.org/2/library/json.html#json.dump\n\n    @since version 1.0.0; indentation character @a indent_char, option\n           @a ensure_ascii and exceptions added in version 3.0.0; error\n           handlers added in version 3.4.0.\n    */\n    string_t dump(const int indent = -1,\n                  const char indent_char = ' ',\n                  const bool ensure_ascii = false,\n                  const error_handler_t error_handler = error_handler_t::strict) const\n    {\n        string_t result;\n        serializer s(detail::output_adapter<char, string_t>(result), indent_char, error_handler);\n\n        if (indent >= 0)\n        {\n            s.dump(*this, true, ensure_ascii, static_cast<unsigned int>(indent));\n        }\n        else\n        {\n            s.dump(*this, false, ensure_ascii, 0);\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief return the type of the JSON value (explicit)\n\n    Return the type of the JSON value as a value from the @ref value_t\n    enumeration.\n\n    @return the type of the JSON value\n            Value type                | return value\n            ------------------------- | -------------------------\n            null                      | value_t::null\n            boolean                   | value_t::boolean\n            string                    | value_t::string\n            number (integer)          | value_t::number_integer\n            number (unsigned integer) | value_t::number_unsigned\n            number (floating-point)   | value_t::number_float\n            object                    | value_t::object\n            array                     | value_t::array\n            discarded                 | value_t::discarded\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `type()` for all JSON\n    types.,type}\n\n    @sa @ref operator value_t() -- return the type of the JSON value (implicit)\n    @sa @ref type_name() -- return the type as string\n\n    @since version 1.0.0\n    */\n    constexpr value_t type() const noexcept\n    {\n        return m_type;\n    }\n\n    /*!\n    @brief return whether type is primitive\n\n    This function returns true if and only if the JSON type is primitive\n    (string, number, boolean, or null).\n\n    @return `true` if type is primitive (string, number, boolean, or null),\n    `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_primitive()` for all JSON\n    types.,is_primitive}\n\n    @sa @ref is_structured() -- returns whether JSON value is structured\n    @sa @ref is_null() -- returns whether JSON value is `null`\n    @sa @ref is_string() -- returns whether JSON value is a string\n    @sa @ref is_boolean() -- returns whether JSON value is a boolean\n    @sa @ref is_number() -- returns whether JSON value is a number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_primitive() const noexcept\n    {\n        return is_null() or is_string() or is_boolean() or is_number();\n    }\n\n    /*!\n    @brief return whether type is structured\n\n    This function returns true if and only if the JSON type is structured\n    (array or object).\n\n    @return `true` if type is structured (array or object), `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_structured()` for all JSON\n    types.,is_structured}\n\n    @sa @ref is_primitive() -- returns whether value is primitive\n    @sa @ref is_array() -- returns whether value is an array\n    @sa @ref is_object() -- returns whether value is an object\n\n    @since version 1.0.0\n    */\n    constexpr bool is_structured() const noexcept\n    {\n        return is_array() or is_object();\n    }\n\n    /*!\n    @brief return whether value is null\n\n    This function returns true if and only if the JSON value is null.\n\n    @return `true` if type is null, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_null()` for all JSON\n    types.,is_null}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_null() const noexcept\n    {\n        return (m_type == value_t::null);\n    }\n\n    /*!\n    @brief return whether value is a boolean\n\n    This function returns true if and only if the JSON value is a boolean.\n\n    @return `true` if type is boolean, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_boolean()` for all JSON\n    types.,is_boolean}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_boolean() const noexcept\n    {\n        return (m_type == value_t::boolean);\n    }\n\n    /*!\n    @brief return whether value is a number\n\n    This function returns true if and only if the JSON value is a number. This\n    includes both integer (signed and unsigned) and floating-point values.\n\n    @return `true` if type is number (regardless whether integer, unsigned\n    integer or floating-type), `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number()` for all JSON\n    types.,is_number}\n\n    @sa @ref is_number_integer() -- check if value is an integer or unsigned\n    integer number\n    @sa @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n    @sa @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number() const noexcept\n    {\n        return is_number_integer() or is_number_float();\n    }\n\n    /*!\n    @brief return whether value is an integer number\n\n    This function returns true if and only if the JSON value is a signed or\n    unsigned integer number. This excludes floating-point values.\n\n    @return `true` if type is an integer or unsigned integer number, `false`\n    otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_integer()` for all\n    JSON types.,is_number_integer}\n\n    @sa @ref is_number() -- check if value is a number\n    @sa @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n    @sa @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number_integer() const noexcept\n    {\n        return (m_type == value_t::number_integer or m_type == value_t::number_unsigned);\n    }\n\n    /*!\n    @brief return whether value is an unsigned integer number\n\n    This function returns true if and only if the JSON value is an unsigned\n    integer number. This excludes floating-point and signed integer values.\n\n    @return `true` if type is an unsigned integer number, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_unsigned()` for all\n    JSON types.,is_number_unsigned}\n\n    @sa @ref is_number() -- check if value is a number\n    @sa @ref is_number_integer() -- check if value is an integer or unsigned\n    integer number\n    @sa @ref is_number_float() -- check if value is a floating-point number\n\n    @since version 2.0.0\n    */\n    constexpr bool is_number_unsigned() const noexcept\n    {\n        return (m_type == value_t::number_unsigned);\n    }\n\n    /*!\n    @brief return whether value is a floating-point number\n\n    This function returns true if and only if the JSON value is a\n    floating-point number. This excludes signed and unsigned integer values.\n\n    @return `true` if type is a floating-point number, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_number_float()` for all\n    JSON types.,is_number_float}\n\n    @sa @ref is_number() -- check if value is number\n    @sa @ref is_number_integer() -- check if value is an integer number\n    @sa @ref is_number_unsigned() -- check if value is an unsigned integer\n    number\n\n    @since version 1.0.0\n    */\n    constexpr bool is_number_float() const noexcept\n    {\n        return (m_type == value_t::number_float);\n    }\n\n    /*!\n    @brief return whether value is an object\n\n    This function returns true if and only if the JSON value is an object.\n\n    @return `true` if type is object, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_object()` for all JSON\n    types.,is_object}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_object() const noexcept\n    {\n        return (m_type == value_t::object);\n    }\n\n    /*!\n    @brief return whether value is an array\n\n    This function returns true if and only if the JSON value is an array.\n\n    @return `true` if type is array, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_array()` for all JSON\n    types.,is_array}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_array() const noexcept\n    {\n        return (m_type == value_t::array);\n    }\n\n    /*!\n    @brief return whether value is a string\n\n    This function returns true if and only if the JSON value is a string.\n\n    @return `true` if type is string, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_string()` for all JSON\n    types.,is_string}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_string() const noexcept\n    {\n        return (m_type == value_t::string);\n    }\n\n    /*!\n    @brief return whether value is discarded\n\n    This function returns true if and only if the JSON value was discarded\n    during parsing with a callback function (see @ref parser_callback_t).\n\n    @note This function will always be `false` for JSON values after parsing.\n    That is, discarded values can only occur during parsing, but will be\n    removed when inside a structured value or replaced by null in other cases.\n\n    @return `true` if type is discarded, `false` otherwise.\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies `is_discarded()` for all JSON\n    types.,is_discarded}\n\n    @since version 1.0.0\n    */\n    constexpr bool is_discarded() const noexcept\n    {\n        return (m_type == value_t::discarded);\n    }\n\n    /*!\n    @brief return the type of the JSON value (implicit)\n\n    Implicitly return the type of the JSON value as a value from the @ref\n    value_t enumeration.\n\n    @return the type of the JSON value\n\n    @complexity Constant.\n\n    @exceptionsafety No-throw guarantee: this member function never throws\n    exceptions.\n\n    @liveexample{The following code exemplifies the @ref value_t operator for\n    all JSON types.,operator__value_t}\n\n    @sa @ref type() -- return the type of the JSON value (explicit)\n    @sa @ref type_name() -- return the type as string\n\n    @since version 1.0.0\n    */\n    constexpr operator value_t() const noexcept\n    {\n        return m_type;\n    }\n\n    /// @}\n\n  private:\n    //////////////////\n    // value access //\n    //////////////////\n\n    /// get a boolean (explicit)\n    boolean_t get_impl(boolean_t* /*unused*/) const\n    {\n        if (JSON_LIKELY(is_boolean()))\n        {\n            return m_value.boolean;\n        }\n\n        JSON_THROW(type_error::create(302, \"type must be boolean, but is \" + std::string(type_name())));\n    }\n\n    /// get a pointer to the value (object)\n    object_t* get_impl_ptr(object_t* /*unused*/) noexcept\n    {\n        return is_object() ? m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (object)\n    constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept\n    {\n        return is_object() ? m_value.object : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    array_t* get_impl_ptr(array_t* /*unused*/) noexcept\n    {\n        return is_array() ? m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (array)\n    constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept\n    {\n        return is_array() ? m_value.array : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    string_t* get_impl_ptr(string_t* /*unused*/) noexcept\n    {\n        return is_string() ? m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (string)\n    constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept\n    {\n        return is_string() ? m_value.string : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept\n    {\n        return is_boolean() ? &m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (boolean)\n    constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept\n    {\n        return is_boolean() ? &m_value.boolean : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept\n    {\n        return is_number_integer() ? &m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (integer number)\n    constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept\n    {\n        return is_number_integer() ? &m_value.number_integer : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept\n    {\n        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (unsigned number)\n    constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept\n    {\n        return is_number_unsigned() ? &m_value.number_unsigned : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept\n    {\n        return is_number_float() ? &m_value.number_float : nullptr;\n    }\n\n    /// get a pointer to the value (floating-point number)\n    constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept\n    {\n        return is_number_float() ? &m_value.number_float : nullptr;\n    }\n\n    /*!\n    @brief helper function to implement get_ref()\n\n    This function helps to implement get_ref() without code duplication for\n    const and non-const overloads\n\n    @tparam ThisType will be deduced as `basic_json` or `const basic_json`\n\n    @throw type_error.303 if ReferenceType does not match underlying value\n    type of the current JSON\n    */\n    template<typename ReferenceType, typename ThisType>\n    static ReferenceType get_ref_impl(ThisType& obj)\n    {\n        // delegate the call to get_ptr<>()\n        auto ptr = obj.template get_ptr<typename std::add_pointer<ReferenceType>::type>();\n\n        if (JSON_LIKELY(ptr != nullptr))\n        {\n            return *ptr;\n        }\n\n        JSON_THROW(type_error::create(303, \"incompatible ReferenceType for get_ref, actual type is \" + std::string(obj.type_name())));\n    }\n\n  public:\n    /// @name value access\n    /// Direct access to the stored value of a JSON value.\n    /// @{\n\n    /*!\n    @brief get special-case overload\n\n    This overloads avoids a lot of template boilerplate, it can be seen as the\n    identity method\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this\n\n    @complexity Constant.\n\n    @since version 2.1.0\n    */\n    template<typename BasicJsonType, detail::enable_if_t<\n                 std::is_same<typename std::remove_const<BasicJsonType>::type, basic_json_t>::value,\n                 int> = 0>\n    basic_json get() const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get special-case overload\n\n    This overloads converts the current @ref basic_json in a different\n    @ref basic_json type\n\n    @tparam BasicJsonType == @ref basic_json\n\n    @return a copy of *this, converted into @tparam BasicJsonType\n\n    @complexity Depending on the implementation of the called `from_json()`\n                method.\n\n    @since version 3.2.0\n    */\n    template<typename BasicJsonType, detail::enable_if_t<\n                 not std::is_same<BasicJsonType, basic_json>::value and\n                 detail::is_basic_json<BasicJsonType>::value, int> = 0>\n    BasicJsonType get() const\n    {\n        return *this;\n    }\n\n    /*!\n    @brief get a value (explicit)\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    ValueType ret;\n    JSONSerializer<ValueType>::from_json(*this, ret);\n    return ret;\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json,\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `void from_json(const basic_json&, ValueType&)`, and\n    - @ref json_serializer<ValueType> does not have a `from_json()` method of\n      the form `ValueType from_json(const basic_json&)`\n\n    @tparam ValueTypeCV the provided value type\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,get__ValueType_const}\n\n    @since version 2.1.0\n    */\n    template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,\n             detail::enable_if_t <\n                 not detail::is_basic_json<ValueType>::value and\n                 detail::has_from_json<basic_json_t, ValueType>::value and\n                 not detail::has_non_default_from_json<basic_json_t, ValueType>::value,\n                 int> = 0>\n    ValueType get() const noexcept(noexcept(\n                                       JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), std::declval<ValueType&>())))\n    {\n        // we cannot static_assert on ValueTypeCV being non-const, because\n        // there is support for get<const basic_json_t>(), which is why we\n        // still need the uncvref\n        static_assert(not std::is_reference<ValueTypeCV>::value,\n                      \"get() cannot be used with reference types, you might want to use get_ref()\");\n        static_assert(std::is_default_constructible<ValueType>::value,\n                      \"types must be DefaultConstructible when used with get()\");\n\n        ValueType ret;\n        JSONSerializer<ValueType>::from_json(*this, ret);\n        return ret;\n    }\n\n    /*!\n    @brief get a value (explicit); special case\n\n    Explicit type conversion between the JSON value and a compatible value\n    which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible)\n    and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible).\n    The value is converted by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    return JSONSerializer<ValueTypeCV>::from_json(*this);\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json and\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `ValueType from_json(const basic_json&)`\n\n    @note If @ref json_serializer<ValueType> has both overloads of\n    `from_json()`, this one is chosen.\n\n    @tparam ValueTypeCV the provided value type\n    @tparam ValueType the returned value type\n\n    @return copy of the JSON value, converted to @a ValueType\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @since version 2.1.0\n    */\n    template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,\n             detail::enable_if_t<not std::is_same<basic_json_t, ValueType>::value and\n                                 detail::has_non_default_from_json<basic_json_t, ValueType>::value,\n                                 int> = 0>\n    ValueType get() const noexcept(noexcept(\n                                       JSONSerializer<ValueTypeCV>::from_json(std::declval<const basic_json_t&>())))\n    {\n        static_assert(not std::is_reference<ValueTypeCV>::value,\n                      \"get() cannot be used with reference types, you might want to use get_ref()\");\n        return JSONSerializer<ValueTypeCV>::from_json(*this);\n    }\n\n    /*!\n    @brief get a value (explicit)\n\n    Explicit type conversion between the JSON value and a compatible value.\n    The value is filled into the input parameter by calling the @ref json_serializer<ValueType>\n    `from_json()` method.\n\n    The function is equivalent to executing\n    @code {.cpp}\n    ValueType v;\n    JSONSerializer<ValueType>::from_json(*this, v);\n    @endcode\n\n    This overloads is chosen if:\n    - @a ValueType is not @ref basic_json,\n    - @ref json_serializer<ValueType> has a `from_json()` method of the form\n      `void from_json(const basic_json&, ValueType&)`, and\n\n    @tparam ValueType the input parameter type.\n\n    @return the input parameter, allowing chaining calls.\n\n    @throw what @ref json_serializer<ValueType> `from_json()` method throws\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,get_to}\n\n    @since version 3.3.0\n    */\n    template<typename ValueType,\n             detail::enable_if_t <\n                 not detail::is_basic_json<ValueType>::value and\n                 detail::has_from_json<basic_json_t, ValueType>::value,\n                 int> = 0>\n    ValueType & get_to(ValueType& v) const noexcept(noexcept(\n                JSONSerializer<ValueType>::from_json(std::declval<const basic_json_t&>(), v)))\n    {\n        JSONSerializer<ValueType>::from_json(*this, v);\n        return v;\n    }\n\n\n    /*!\n    @brief get a pointer value (implicit)\n\n    Implicit pointer access to the internally stored JSON value. No copies are\n    made.\n\n    @warning Writing data to the pointee of the result yields an undefined\n    state.\n\n    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref\n    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,\n    @ref number_unsigned_t, or @ref number_float_t. Enforced by a static\n    assertion.\n\n    @return pointer to the internally stored JSON value if the requested\n    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how pointers to internal values of a\n    JSON value can be requested. Note that no type conversions are made and a\n    `nullptr` is returned if the value and the requested pointer type does not\n    match.,get_ptr}\n\n    @since version 1.0.0\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get_ptr() noexcept -> decltype(std::declval<basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>()\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n    /*!\n    @brief get a pointer value (implicit)\n    @copydoc get_ptr()\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value and\n                 std::is_const<typename std::remove_pointer<PointerType>::type>::value, int>::type = 0>\n    constexpr auto get_ptr() const noexcept -> decltype(std::declval<const basic_json_t&>().get_impl_ptr(std::declval<PointerType>()))\n    {\n        // delegate the call to get_impl_ptr<>() const\n        return get_impl_ptr(static_cast<PointerType>(nullptr));\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n\n    Explicit pointer access to the internally stored JSON value. No copies are\n    made.\n\n    @warning The pointer becomes invalid if the underlying JSON object\n    changes.\n\n    @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref\n    object_t, @ref string_t, @ref boolean_t, @ref number_integer_t,\n    @ref number_unsigned_t, or @ref number_float_t.\n\n    @return pointer to the internally stored JSON value if the requested\n    pointer type @a PointerType fits to the JSON value; `nullptr` otherwise\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how pointers to internal values of a\n    JSON value can be requested. Note that no type conversions are made and a\n    `nullptr` is returned if the value and the requested pointer type does not\n    match.,get__PointerType}\n\n    @sa @ref get_ptr() for explicit pointer-member access\n\n    @since version 1.0.0\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    auto get() noexcept -> decltype(std::declval<basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n    /*!\n    @brief get a pointer value (explicit)\n    @copydoc get()\n    */\n    template<typename PointerType, typename std::enable_if<\n                 std::is_pointer<PointerType>::value, int>::type = 0>\n    constexpr auto get() const noexcept -> decltype(std::declval<const basic_json_t&>().template get_ptr<PointerType>())\n    {\n        // delegate the call to get_ptr\n        return get_ptr<PointerType>();\n    }\n\n    /*!\n    @brief get a reference value (implicit)\n\n    Implicit reference access to the internally stored JSON value. No copies\n    are made.\n\n    @warning Writing data to the referee of the result yields an undefined\n    state.\n\n    @tparam ReferenceType reference type; must be a reference to @ref array_t,\n    @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or\n    @ref number_float_t. Enforced by static assertion.\n\n    @return reference to the internally stored JSON value if the requested\n    reference type @a ReferenceType fits to the JSON value; throws\n    type_error.303 otherwise\n\n    @throw type_error.303 in case passed type @a ReferenceType is incompatible\n    with the stored JSON value; see example below\n\n    @complexity Constant.\n\n    @liveexample{The example shows several calls to `get_ref()`.,get_ref}\n\n    @since version 1.1.0\n    */\n    template<typename ReferenceType, typename std::enable_if<\n                 std::is_reference<ReferenceType>::value, int>::type = 0>\n    ReferenceType get_ref()\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /*!\n    @brief get a reference value (implicit)\n    @copydoc get_ref()\n    */\n    template<typename ReferenceType, typename std::enable_if<\n                 std::is_reference<ReferenceType>::value and\n                 std::is_const<typename std::remove_reference<ReferenceType>::type>::value, int>::type = 0>\n    ReferenceType get_ref() const\n    {\n        // delegate call to get_ref_impl\n        return get_ref_impl<ReferenceType>(*this);\n    }\n\n    /*!\n    @brief get a value (implicit)\n\n    Implicit type conversion between the JSON value and a compatible value.\n    The call is realized by calling @ref get() const.\n\n    @tparam ValueType non-pointer type compatible to the JSON value, for\n    instance `int` for JSON integer numbers, `bool` for JSON booleans, or\n    `std::vector` types for JSON arrays. The character type of @ref string_t\n    as well as an initializer list of this type is excluded to avoid\n    ambiguities as these types implicitly convert to `std::string`.\n\n    @return copy of the JSON value, converted to type @a ValueType\n\n    @throw type_error.302 in case passed type @a ValueType is incompatible\n    to the JSON value type (e.g., the JSON value is of type boolean, but a\n    string is requested); see example below\n\n    @complexity Linear in the size of the JSON value.\n\n    @liveexample{The example below shows several conversions from JSON values\n    to other types. There a few things to note: (1) Floating-point numbers can\n    be converted to integers\\, (2) A JSON array can be converted to a standard\n    `std::vector<short>`\\, (3) A JSON object can be converted to C++\n    associative containers such as `std::unordered_map<std::string\\,\n    json>`.,operator__ValueType}\n\n    @since version 1.0.0\n    */\n    template < typename ValueType, typename std::enable_if <\n                   not std::is_pointer<ValueType>::value and\n                   not std::is_same<ValueType, detail::json_ref<basic_json>>::value and\n                   not std::is_same<ValueType, typename string_t::value_type>::value and\n                   not detail::is_basic_json<ValueType>::value\n\n#ifndef _MSC_VER  // fix for issue #167 operator<< ambiguity under VS2015\n                   and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value\n#if defined(JSON_HAS_CPP_17) && defined(_MSC_VER) and _MSC_VER <= 1914\n                   and not std::is_same<ValueType, typename std::string_view>::value\n#endif\n#endif\n                   and detail::is_detected<detail::get_template_function, const basic_json_t&, ValueType>::value\n                   , int >::type = 0 >\n    operator ValueType() const\n    {\n        // delegate the call to get<>() const\n        return get<ValueType>();\n    }\n\n    /// @}\n\n\n    ////////////////////\n    // element access //\n    ////////////////////\n\n    /// @name element access\n    /// Access to the JSON value.\n    /// @{\n\n    /*!\n    @brief access specified array element with bounds checking\n\n    Returns a reference to the element at specified location @a idx, with\n    bounds checking.\n\n    @param[in] idx  index of the element to access\n\n    @return reference to the element at index @a idx\n\n    @throw type_error.304 if the JSON value is not an array; in this case,\n    calling `at` with an index makes no sense. See example below.\n    @throw out_of_range.401 if the index @a idx is out of range of the array;\n    that is, `idx >= size()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how array elements can be read and\n    written using `at()`. It also demonstrates the different exceptions that\n    can be thrown.,at__size_type}\n    */\n    reference at(size_type idx)\n    {\n        // at only works for arrays\n        if (JSON_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return m_value.array->at(idx);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified array element with bounds checking\n\n    Returns a const reference to the element at specified location @a idx,\n    with bounds checking.\n\n    @param[in] idx  index of the element to access\n\n    @return const reference to the element at index @a idx\n\n    @throw type_error.304 if the JSON value is not an array; in this case,\n    calling `at` with an index makes no sense. See example below.\n    @throw out_of_range.401 if the index @a idx is out of range of the array;\n    that is, `idx >= size()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how array elements can be read using\n    `at()`. It also demonstrates the different exceptions that can be thrown.,\n    at__size_type_const}\n    */\n    const_reference at(size_type idx) const\n    {\n        // at only works for arrays\n        if (JSON_LIKELY(is_array()))\n        {\n            JSON_TRY\n            {\n                return m_value.array->at(idx);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified object element with bounds checking\n\n    Returns a reference to the element at with specified key @a key, with\n    bounds checking.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.304 if the JSON value is not an object; in this case,\n    calling `at` with a key makes no sense. See example below.\n    @throw out_of_range.403 if the key @a key is is not stored in the object;\n    that is, `find(key) == end()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Logarithmic in the size of the container.\n\n    @sa @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how object elements can be read and\n    written using `at()`. It also demonstrates the different exceptions that\n    can be thrown.,at__object_t_key_type}\n    */\n    reference at(const typename object_t::key_type& key)\n    {\n        // at only works for objects\n        if (JSON_LIKELY(is_object()))\n        {\n            JSON_TRY\n            {\n                return m_value.object->at(key);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(403, \"key '\" + key + \"' not found\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified object element with bounds checking\n\n    Returns a const reference to the element at with specified key @a key,\n    with bounds checking.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @throw type_error.304 if the JSON value is not an object; in this case,\n    calling `at` with a key makes no sense. See example below.\n    @throw out_of_range.403 if the key @a key is is not stored in the object;\n    that is, `find(key) == end()`. See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Logarithmic in the size of the container.\n\n    @sa @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n\n    @liveexample{The example below shows how object elements can be read using\n    `at()`. It also demonstrates the different exceptions that can be thrown.,\n    at__object_t_key_type_const}\n    */\n    const_reference at(const typename object_t::key_type& key) const\n    {\n        // at only works for objects\n        if (JSON_LIKELY(is_object()))\n        {\n            JSON_TRY\n            {\n                return m_value.object->at(key);\n            }\n            JSON_CATCH (std::out_of_range&)\n            {\n                // create better exception explanation\n                JSON_THROW(out_of_range::create(403, \"key '\" + key + \"' not found\"));\n            }\n        }\n        else\n        {\n            JSON_THROW(type_error::create(304, \"cannot use at() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief access specified array element\n\n    Returns a reference to the element at specified location @a idx.\n\n    @note If @a idx is beyond the range of the array (i.e., `idx >= size()`),\n    then the array is silently filled up with `null` values to make `idx` a\n    valid reference to the last stored element.\n\n    @param[in] idx  index of the element to access\n\n    @return reference to the element at index @a idx\n\n    @throw type_error.305 if the JSON value is not an array or null; in that\n    cases, using the [] operator with an index makes no sense.\n\n    @complexity Constant if @a idx is in the range of the array. Otherwise\n    linear in `idx - size()`.\n\n    @liveexample{The example below shows how array elements can be read and\n    written using `[]` operator. Note the addition of `null`\n    values.,operatorarray__size_type}\n\n    @since version 1.0.0\n    */\n    reference operator[](size_type idx)\n    {\n        // implicitly convert null value to an empty array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value.array = create<array_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for arrays\n        if (JSON_LIKELY(is_array()))\n        {\n            // fill up array with null values if given idx is outside range\n            if (idx >= m_value.array->size())\n            {\n                m_value.array->insert(m_value.array->end(),\n                                      idx - m_value.array->size() + 1,\n                                      basic_json());\n            }\n\n            return m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a numeric argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified array element\n\n    Returns a const reference to the element at specified location @a idx.\n\n    @param[in] idx  index of the element to access\n\n    @return const reference to the element at index @a idx\n\n    @throw type_error.305 if the JSON value is not an array; in that case,\n    using the [] operator with an index makes no sense.\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how array elements can be read using\n    the `[]` operator.,operatorarray__size_type_const}\n\n    @since version 1.0.0\n    */\n    const_reference operator[](size_type idx) const\n    {\n        // const operator[] only works for arrays\n        if (JSON_LIKELY(is_array()))\n        {\n            return m_value.array->operator[](idx);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a numeric argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified object element\n\n    Returns a reference to the element at with specified key @a key.\n\n    @note If @a key is not found in the object, then it is silently added to\n    the object and filled with a `null` value to make `key` a valid reference.\n    In case the value was `null` before, it is converted to an object.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.305 if the JSON value is not an object or null; in that\n    cases, using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read and\n    written using the `[]` operator.,operatorarray__key_type}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n    */\n    reference operator[](const typename object_t::key_type& key)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        // operator[] only works for objects\n        if (JSON_LIKELY(is_object()))\n        {\n            return m_value.object->operator[](key);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief read-only access specified object element\n\n    Returns a const reference to the element at with specified key @a key. No\n    bounds checking is performed.\n\n    @warning If the element with key @a key does not exist, the behavior is\n    undefined.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @pre The element with key @a key must exist. **This precondition is\n         enforced with an assertion.**\n\n    @throw type_error.305 if the JSON value is not an object; in that case,\n    using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read using\n    the `[]` operator.,operatorarray__key_type_const}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.0.0\n    */\n    const_reference operator[](const typename object_t::key_type& key) const\n    {\n        // const operator[] only works for objects\n        if (JSON_LIKELY(is_object()))\n        {\n            assert(m_value.object->find(key) != m_value.object->end());\n            return m_value.object->find(key)->second;\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified object element\n\n    Returns a reference to the element at with specified key @a key.\n\n    @note If @a key is not found in the object, then it is silently added to\n    the object and filled with a `null` value to make `key` a valid reference.\n    In case the value was `null` before, it is converted to an object.\n\n    @param[in] key  key of the element to access\n\n    @return reference to the element at key @a key\n\n    @throw type_error.305 if the JSON value is not an object or null; in that\n    cases, using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read and\n    written using the `[]` operator.,operatorarray__key_type}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.1.0\n    */\n    template<typename T>\n    reference operator[](T* key)\n    {\n        // implicitly convert null to object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // at only works for objects\n        if (JSON_LIKELY(is_object()))\n        {\n            return m_value.object->operator[](key);\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief read-only access specified object element\n\n    Returns a const reference to the element at with specified key @a key. No\n    bounds checking is performed.\n\n    @warning If the element with key @a key does not exist, the behavior is\n    undefined.\n\n    @param[in] key  key of the element to access\n\n    @return const reference to the element at key @a key\n\n    @pre The element with key @a key must exist. **This precondition is\n         enforced with an assertion.**\n\n    @throw type_error.305 if the JSON value is not an object; in that case,\n    using the [] operator with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be read using\n    the `[]` operator.,operatorarray__key_type_const}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref value() for access by value with a default value\n\n    @since version 1.1.0\n    */\n    template<typename T>\n    const_reference operator[](T* key) const\n    {\n        // at only works for objects\n        if (JSON_LIKELY(is_object()))\n        {\n            assert(m_value.object->find(key) != m_value.object->end());\n            return m_value.object->find(key)->second;\n        }\n\n        JSON_THROW(type_error::create(305, \"cannot use operator[] with a string argument with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief access specified object element with default value\n\n    Returns either a copy of an object's element at the specified key @a key\n    or a given default value if no element with key @a key exists.\n\n    The function is basically equivalent to executing\n    @code {.cpp}\n    try {\n        return at(key);\n    } catch(out_of_range) {\n        return default_value;\n    }\n    @endcode\n\n    @note Unlike @ref at(const typename object_t::key_type&), this function\n    does not throw if the given key @a key was not found.\n\n    @note Unlike @ref operator[](const typename object_t::key_type& key), this\n    function does not implicitly add an element to the position defined by @a\n    key. This function is furthermore also applicable to const objects.\n\n    @param[in] key  key of the element to access\n    @param[in] default_value  the value to return if @a key is not found\n\n    @tparam ValueType type compatible to JSON values, for instance `int` for\n    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for\n    JSON arrays. Note the type of the expected value at @a key and the default\n    value @a default_value must be compatible.\n\n    @return copy of the element at key @a key or @a default_value if @a key\n    is not found\n\n    @throw type_error.306 if the JSON value is not an object; in that case,\n    using `value()` with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be queried\n    with a default value.,basic_json__value}\n\n    @sa @ref at(const typename object_t::key_type&) for access by reference\n    with range checking\n    @sa @ref operator[](const typename object_t::key_type&) for unchecked\n    access by reference\n\n    @since version 1.0.0\n    */\n    template<class ValueType, typename std::enable_if<\n                 std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>\n    ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const\n    {\n        // at only works for objects\n        if (JSON_LIKELY(is_object()))\n        {\n            // if key is found, return value and given default value otherwise\n            const auto it = find(key);\n            if (it != end())\n            {\n                return *it;\n            }\n\n            return default_value;\n        }\n\n        JSON_THROW(type_error::create(306, \"cannot use value() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief overload for a default value of type const char*\n    @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const\n    */\n    string_t value(const typename object_t::key_type& key, const char* default_value) const\n    {\n        return value(key, string_t(default_value));\n    }\n\n    /*!\n    @brief access specified object element via JSON Pointer with default value\n\n    Returns either a copy of an object's element at the specified key @a key\n    or a given default value if no element with key @a key exists.\n\n    The function is basically equivalent to executing\n    @code {.cpp}\n    try {\n        return at(ptr);\n    } catch(out_of_range) {\n        return default_value;\n    }\n    @endcode\n\n    @note Unlike @ref at(const json_pointer&), this function does not throw\n    if the given key @a key was not found.\n\n    @param[in] ptr  a JSON pointer to the element to access\n    @param[in] default_value  the value to return if @a ptr found no value\n\n    @tparam ValueType type compatible to JSON values, for instance `int` for\n    JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for\n    JSON arrays. Note the type of the expected value at @a key and the default\n    value @a default_value must be compatible.\n\n    @return copy of the element at key @a key or @a default_value if @a key\n    is not found\n\n    @throw type_error.306 if the JSON value is not an object; in that case,\n    using `value()` with a key makes no sense.\n\n    @complexity Logarithmic in the size of the container.\n\n    @liveexample{The example below shows how object elements can be queried\n    with a default value.,basic_json__value_ptr}\n\n    @sa @ref operator[](const json_pointer&) for unchecked access by reference\n\n    @since version 2.0.2\n    */\n    template<class ValueType, typename std::enable_if<\n                 std::is_convertible<basic_json_t, ValueType>::value, int>::type = 0>\n    ValueType value(const json_pointer& ptr, const ValueType& default_value) const\n    {\n        // at only works for objects\n        if (JSON_LIKELY(is_object()))\n        {\n            // if pointer resolves a value, return it or use default value\n            JSON_TRY\n            {\n                return ptr.get_checked(this);\n            }\n            JSON_INTERNAL_CATCH (out_of_range&)\n            {\n                return default_value;\n            }\n        }\n\n        JSON_THROW(type_error::create(306, \"cannot use value() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief overload for a default value of type const char*\n    @copydoc basic_json::value(const json_pointer&, ValueType) const\n    */\n    string_t value(const json_pointer& ptr, const char* default_value) const\n    {\n        return value(ptr, string_t(default_value));\n    }\n\n    /*!\n    @brief access the first element\n\n    Returns a reference to the first element in the container. For a JSON\n    container `c`, the expression `c.front()` is equivalent to `*c.begin()`.\n\n    @return In case of a structured type (array or object), a reference to the\n    first element is returned. In case of number, string, or boolean values, a\n    reference to the value is returned.\n\n    @complexity Constant.\n\n    @pre The JSON value must not be `null` (would throw `std::out_of_range`)\n    or an empty array or object (undefined behavior, **guarded by\n    assertions**).\n    @post The JSON value remains unchanged.\n\n    @throw invalid_iterator.214 when called on `null` value\n\n    @liveexample{The following code shows an example for `front()`.,front}\n\n    @sa @ref back() -- access the last element\n\n    @since version 1.0.0\n    */\n    reference front()\n    {\n        return *begin();\n    }\n\n    /*!\n    @copydoc basic_json::front()\n    */\n    const_reference front() const\n    {\n        return *cbegin();\n    }\n\n    /*!\n    @brief access the last element\n\n    Returns a reference to the last element in the container. For a JSON\n    container `c`, the expression `c.back()` is equivalent to\n    @code {.cpp}\n    auto tmp = c.end();\n    --tmp;\n    return *tmp;\n    @endcode\n\n    @return In case of a structured type (array or object), a reference to the\n    last element is returned. In case of number, string, or boolean values, a\n    reference to the value is returned.\n\n    @complexity Constant.\n\n    @pre The JSON value must not be `null` (would throw `std::out_of_range`)\n    or an empty array or object (undefined behavior, **guarded by\n    assertions**).\n    @post The JSON value remains unchanged.\n\n    @throw invalid_iterator.214 when called on a `null` value. See example\n    below.\n\n    @liveexample{The following code shows an example for `back()`.,back}\n\n    @sa @ref front() -- access the first element\n\n    @since version 1.0.0\n    */\n    reference back()\n    {\n        auto tmp = end();\n        --tmp;\n        return *tmp;\n    }\n\n    /*!\n    @copydoc basic_json::back()\n    */\n    const_reference back() const\n    {\n        auto tmp = cend();\n        --tmp;\n        return *tmp;\n    }\n\n    /*!\n    @brief remove element given an iterator\n\n    Removes the element specified by iterator @a pos. The iterator @a pos must\n    be valid and dereferenceable. Thus the `end()` iterator (which is valid,\n    but is not dereferenceable) cannot be used as a value for @a pos.\n\n    If called on a primitive type other than `null`, the resulting JSON value\n    will be `null`.\n\n    @param[in] pos iterator to the element to remove\n    @return Iterator following the last removed element. If the iterator @a\n    pos refers to the last element, the `end()` iterator is returned.\n\n    @tparam IteratorType an @ref iterator or @ref const_iterator\n\n    @post Invalidates iterators and references at or after the point of the\n    erase, including the `end()` iterator.\n\n    @throw type_error.307 if called on a `null` value; example: `\"cannot use\n    erase() with null\"`\n    @throw invalid_iterator.202 if called on an iterator which does not belong\n    to the current JSON value; example: `\"iterator does not fit current\n    value\"`\n    @throw invalid_iterator.205 if called on a primitive type with invalid\n    iterator (i.e., any iterator which is not `begin()`); example: `\"iterator\n    out of range\"`\n\n    @complexity The complexity depends on the type:\n    - objects: amortized constant\n    - arrays: linear in distance between @a pos and the end of the container\n    - strings: linear in the length of the string\n    - other types: constant\n\n    @liveexample{The example shows the result of `erase()` for different JSON\n    types.,erase__IteratorType}\n\n    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n    @sa @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    template<class IteratorType, typename std::enable_if<\n                 std::is_same<IteratorType, typename basic_json_t::iterator>::value or\n                 std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type\n             = 0>\n    IteratorType erase(IteratorType pos)\n    {\n        // make sure iterator fits the current value\n        if (JSON_UNLIKELY(this != pos.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n        }\n\n        IteratorType result = end();\n\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            {\n                if (JSON_UNLIKELY(not pos.m_it.primitive_iterator.is_begin()))\n                {\n                    JSON_THROW(invalid_iterator::create(205, \"iterator out of range\"));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);\n                    m_value.string = nullptr;\n                }\n\n                m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator);\n                break;\n            }\n\n            default:\n                JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief remove elements given an iterator range\n\n    Removes the element specified by the range `[first; last)`. The iterator\n    @a first does not need to be dereferenceable if `first == last`: erasing\n    an empty range is a no-op.\n\n    If called on a primitive type other than `null`, the resulting JSON value\n    will be `null`.\n\n    @param[in] first iterator to the beginning of the range to remove\n    @param[in] last iterator past the end of the range to remove\n    @return Iterator following the last removed element. If the iterator @a\n    second refers to the last element, the `end()` iterator is returned.\n\n    @tparam IteratorType an @ref iterator or @ref const_iterator\n\n    @post Invalidates iterators and references at or after the point of the\n    erase, including the `end()` iterator.\n\n    @throw type_error.307 if called on a `null` value; example: `\"cannot use\n    erase() with null\"`\n    @throw invalid_iterator.203 if called on iterators which does not belong\n    to the current JSON value; example: `\"iterators do not fit current value\"`\n    @throw invalid_iterator.204 if called on a primitive type with invalid\n    iterators (i.e., if `first != begin()` and `last != end()`); example:\n    `\"iterators out of range\"`\n\n    @complexity The complexity depends on the type:\n    - objects: `log(size()) + std::distance(first, last)`\n    - arrays: linear in the distance between @a first and @a last, plus linear\n      in the distance between @a last and end of the container\n    - strings: linear in the length of the string\n    - other types: constant\n\n    @liveexample{The example shows the result of `erase()` for different JSON\n    types.,erase__IteratorType_IteratorType}\n\n    @sa @ref erase(IteratorType) -- removes the element at a given position\n    @sa @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n    @sa @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    template<class IteratorType, typename std::enable_if<\n                 std::is_same<IteratorType, typename basic_json_t::iterator>::value or\n                 std::is_same<IteratorType, typename basic_json_t::const_iterator>::value, int>::type\n             = 0>\n    IteratorType erase(IteratorType first, IteratorType last)\n    {\n        // make sure iterator fits the current value\n        if (JSON_UNLIKELY(this != first.m_object or this != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(203, \"iterators do not fit current value\"));\n        }\n\n        IteratorType result = end();\n\n        switch (m_type)\n        {\n            case value_t::boolean:\n            case value_t::number_float:\n            case value_t::number_integer:\n            case value_t::number_unsigned:\n            case value_t::string:\n            {\n                if (JSON_LIKELY(not first.m_it.primitive_iterator.is_begin()\n                                or not last.m_it.primitive_iterator.is_end()))\n                {\n                    JSON_THROW(invalid_iterator::create(204, \"iterators out of range\"));\n                }\n\n                if (is_string())\n                {\n                    AllocatorType<string_t> alloc;\n                    std::allocator_traits<decltype(alloc)>::destroy(alloc, m_value.string);\n                    std::allocator_traits<decltype(alloc)>::deallocate(alloc, m_value.string, 1);\n                    m_value.string = nullptr;\n                }\n\n                m_type = value_t::null;\n                assert_invariant();\n                break;\n            }\n\n            case value_t::object:\n            {\n                result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator,\n                                              last.m_it.object_iterator);\n                break;\n            }\n\n            case value_t::array:\n            {\n                result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator,\n                                             last.m_it.array_iterator);\n                break;\n            }\n\n            default:\n                JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief remove element from a JSON object given a key\n\n    Removes elements from a JSON object with the key value @a key.\n\n    @param[in] key value of the elements to remove\n\n    @return Number of elements removed. If @a ObjectType is the default\n    `std::map` type, the return value will always be `0` (@a key was not\n    found) or `1` (@a key was found).\n\n    @post References and iterators to the erased elements are invalidated.\n    Other references and iterators are not affected.\n\n    @throw type_error.307 when called on a type other than JSON object;\n    example: `\"cannot use erase() with null\"`\n\n    @complexity `log(size()) + count(key)`\n\n    @liveexample{The example shows the effect of `erase()`.,erase__key_type}\n\n    @sa @ref erase(IteratorType) -- removes the element at a given position\n    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa @ref erase(const size_type) -- removes the element from an array at\n    the given index\n\n    @since version 1.0.0\n    */\n    size_type erase(const typename object_t::key_type& key)\n    {\n        // this erase only works for objects\n        if (JSON_LIKELY(is_object()))\n        {\n            return m_value.object->erase(key);\n        }\n\n        JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief remove element from a JSON array given an index\n\n    Removes element from a JSON array at the index @a idx.\n\n    @param[in] idx index of the element to remove\n\n    @throw type_error.307 when called on a type other than JSON object;\n    example: `\"cannot use erase() with null\"`\n    @throw out_of_range.401 when `idx >= size()`; example: `\"array index 17\n    is out of range\"`\n\n    @complexity Linear in distance between @a idx and the end of the container.\n\n    @liveexample{The example shows the effect of `erase()`.,erase__size_type}\n\n    @sa @ref erase(IteratorType) -- removes the element at a given position\n    @sa @ref erase(IteratorType, IteratorType) -- removes the elements in\n    the given range\n    @sa @ref erase(const typename object_t::key_type&) -- removes the element\n    from an object at the given key\n\n    @since version 1.0.0\n    */\n    void erase(const size_type idx)\n    {\n        // this erase only works for arrays\n        if (JSON_LIKELY(is_array()))\n        {\n            if (JSON_UNLIKELY(idx >= size()))\n            {\n                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n            }\n\n            m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));\n        }\n        else\n        {\n            JSON_THROW(type_error::create(307, \"cannot use erase() with \" + std::string(type_name())));\n        }\n    }\n\n    /// @}\n\n\n    ////////////\n    // lookup //\n    ////////////\n\n    /// @name lookup\n    /// @{\n\n    /*!\n    @brief find an element in a JSON object\n\n    Finds an element in a JSON object with key equivalent to @a key. If the\n    element is not found or the JSON value is not an object, end() is\n    returned.\n\n    @note This method always returns @ref end() when executed on a JSON type\n          that is not an object.\n\n    @param[in] key key value of the element to search for.\n\n    @return Iterator to an element with key equivalent to @a key. If no such\n    element is found or the JSON value is not an object, past-the-end (see\n    @ref end()) iterator is returned.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The example shows how `find()` is used.,find__key_type}\n\n    @since version 1.0.0\n    */\n    template<typename KeyT>\n    iterator find(KeyT&& key)\n    {\n        auto result = end();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief find an element in a JSON object\n    @copydoc find(KeyT&&)\n    */\n    template<typename KeyT>\n    const_iterator find(KeyT&& key) const\n    {\n        auto result = cend();\n\n        if (is_object())\n        {\n            result.m_it.object_iterator = m_value.object->find(std::forward<KeyT>(key));\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief returns the number of occurrences of a key in a JSON object\n\n    Returns the number of elements with key @a key. If ObjectType is the\n    default `std::map` type, the return value will always be `0` (@a key was\n    not found) or `1` (@a key was found).\n\n    @note This method always returns `0` when executed on a JSON type that is\n          not an object.\n\n    @param[in] key key value of the element to count\n\n    @return Number of elements with key @a key. If the JSON value is not an\n    object, the return value will be `0`.\n\n    @complexity Logarithmic in the size of the JSON object.\n\n    @liveexample{The example shows how `count()` is used.,count}\n\n    @since version 1.0.0\n    */\n    template<typename KeyT>\n    size_type count(KeyT&& key) const\n    {\n        // return 0 for all nonobject types\n        return is_object() ? m_value.object->count(std::forward<KeyT>(key)) : 0;\n    }\n\n    /// @}\n\n\n    ///////////////\n    // iterators //\n    ///////////////\n\n    /// @name iterators\n    /// @{\n\n    /*!\n    @brief returns an iterator to the first element\n\n    Returns an iterator to the first element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return iterator to the first element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n\n    @liveexample{The following code shows an example for `begin()`.,begin}\n\n    @sa @ref cbegin() -- returns a const iterator to the beginning\n    @sa @ref end() -- returns an iterator to the end\n    @sa @ref cend() -- returns a const iterator to the end\n\n    @since version 1.0.0\n    */\n    iterator begin() noexcept\n    {\n        iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /*!\n    @copydoc basic_json::cbegin()\n    */\n    const_iterator begin() const noexcept\n    {\n        return cbegin();\n    }\n\n    /*!\n    @brief returns a const iterator to the first element\n\n    Returns a const iterator to the first element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return const iterator to the first element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).begin()`.\n\n    @liveexample{The following code shows an example for `cbegin()`.,cbegin}\n\n    @sa @ref begin() -- returns an iterator to the beginning\n    @sa @ref end() -- returns an iterator to the end\n    @sa @ref cend() -- returns a const iterator to the end\n\n    @since version 1.0.0\n    */\n    const_iterator cbegin() const noexcept\n    {\n        const_iterator result(this);\n        result.set_begin();\n        return result;\n    }\n\n    /*!\n    @brief returns an iterator to one past the last element\n\n    Returns an iterator to one past the last element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return iterator one past the last element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n\n    @liveexample{The following code shows an example for `end()`.,end}\n\n    @sa @ref cend() -- returns a const iterator to the end\n    @sa @ref begin() -- returns an iterator to the beginning\n    @sa @ref cbegin() -- returns a const iterator to the beginning\n\n    @since version 1.0.0\n    */\n    iterator end() noexcept\n    {\n        iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /*!\n    @copydoc basic_json::cend()\n    */\n    const_iterator end() const noexcept\n    {\n        return cend();\n    }\n\n    /*!\n    @brief returns a const iterator to one past the last element\n\n    Returns a const iterator to one past the last element.\n\n    @image html range-begin-end.svg \"Illustration from cppreference.com\"\n\n    @return const iterator one past the last element\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).end()`.\n\n    @liveexample{The following code shows an example for `cend()`.,cend}\n\n    @sa @ref end() -- returns an iterator to the end\n    @sa @ref begin() -- returns an iterator to the beginning\n    @sa @ref cbegin() -- returns a const iterator to the beginning\n\n    @since version 1.0.0\n    */\n    const_iterator cend() const noexcept\n    {\n        const_iterator result(this);\n        result.set_end();\n        return result;\n    }\n\n    /*!\n    @brief returns an iterator to the reverse-beginning\n\n    Returns an iterator to the reverse-beginning; that is, the last element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `reverse_iterator(end())`.\n\n    @liveexample{The following code shows an example for `rbegin()`.,rbegin}\n\n    @sa @ref crbegin() -- returns a const reverse iterator to the beginning\n    @sa @ref rend() -- returns a reverse iterator to the end\n    @sa @ref crend() -- returns a const reverse iterator to the end\n\n    @since version 1.0.0\n    */\n    reverse_iterator rbegin() noexcept\n    {\n        return reverse_iterator(end());\n    }\n\n    /*!\n    @copydoc basic_json::crbegin()\n    */\n    const_reverse_iterator rbegin() const noexcept\n    {\n        return crbegin();\n    }\n\n    /*!\n    @brief returns an iterator to the reverse-end\n\n    Returns an iterator to the reverse-end; that is, one before the first\n    element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `reverse_iterator(begin())`.\n\n    @liveexample{The following code shows an example for `rend()`.,rend}\n\n    @sa @ref crend() -- returns a const reverse iterator to the end\n    @sa @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa @ref crbegin() -- returns a const reverse iterator to the beginning\n\n    @since version 1.0.0\n    */\n    reverse_iterator rend() noexcept\n    {\n        return reverse_iterator(begin());\n    }\n\n    /*!\n    @copydoc basic_json::crend()\n    */\n    const_reverse_iterator rend() const noexcept\n    {\n        return crend();\n    }\n\n    /*!\n    @brief returns a const reverse iterator to the last element\n\n    Returns a const iterator to the reverse-beginning; that is, the last\n    element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).rbegin()`.\n\n    @liveexample{The following code shows an example for `crbegin()`.,crbegin}\n\n    @sa @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa @ref rend() -- returns a reverse iterator to the end\n    @sa @ref crend() -- returns a const reverse iterator to the end\n\n    @since version 1.0.0\n    */\n    const_reverse_iterator crbegin() const noexcept\n    {\n        return const_reverse_iterator(cend());\n    }\n\n    /*!\n    @brief returns a const reverse iterator to one before the first\n\n    Returns a const reverse iterator to the reverse-end; that is, one before\n    the first element.\n\n    @image html range-rbegin-rend.svg \"Illustration from cppreference.com\"\n\n    @complexity Constant.\n\n    @requirement This function helps `basic_json` satisfying the\n    [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `const_cast<const basic_json&>(*this).rend()`.\n\n    @liveexample{The following code shows an example for `crend()`.,crend}\n\n    @sa @ref rend() -- returns a reverse iterator to the end\n    @sa @ref rbegin() -- returns a reverse iterator to the beginning\n    @sa @ref crbegin() -- returns a const reverse iterator to the beginning\n\n    @since version 1.0.0\n    */\n    const_reverse_iterator crend() const noexcept\n    {\n        return const_reverse_iterator(cbegin());\n    }\n\n  public:\n    /*!\n    @brief wrapper to access iterator member functions in range-based for\n\n    This function allows to access @ref iterator::key() and @ref\n    iterator::value() during range-based for loops. In these loops, a\n    reference to the JSON values is returned, so there is no access to the\n    underlying iterator.\n\n    For loop without iterator_wrapper:\n\n    @code{cpp}\n    for (auto it = j_object.begin(); it != j_object.end(); ++it)\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    Range-based for loop without iterator proxy:\n\n    @code{cpp}\n    for (auto it : j_object)\n    {\n        // \"it\" is of type json::reference and has no key() member\n        std::cout << \"value: \" << it << '\\n';\n    }\n    @endcode\n\n    Range-based for loop with iterator proxy:\n\n    @code{cpp}\n    for (auto it : json::iterator_wrapper(j_object))\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    @note When iterating over an array, `key()` will return the index of the\n          element as string (see example).\n\n    @param[in] ref  reference to a JSON value\n    @return iteration proxy object wrapping @a ref with an interface to use in\n            range-based for loops\n\n    @liveexample{The following code shows how the wrapper is used,iterator_wrapper}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @note The name of this function is not yet final and may change in the\n    future.\n\n    @deprecated This stream operator is deprecated and will be removed in\n                future 4.0.0 of the library. Please use @ref items() instead;\n                that is, replace `json::iterator_wrapper(j)` with `j.items()`.\n    */\n    JSON_DEPRECATED\n    static iteration_proxy<iterator> iterator_wrapper(reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /*!\n    @copydoc iterator_wrapper(reference)\n    */\n    JSON_DEPRECATED\n    static iteration_proxy<const_iterator> iterator_wrapper(const_reference ref) noexcept\n    {\n        return ref.items();\n    }\n\n    /*!\n    @brief helper to access iterator member functions in range-based for\n\n    This function allows to access @ref iterator::key() and @ref\n    iterator::value() during range-based for loops. In these loops, a\n    reference to the JSON values is returned, so there is no access to the\n    underlying iterator.\n\n    For loop without `items()` function:\n\n    @code{cpp}\n    for (auto it = j_object.begin(); it != j_object.end(); ++it)\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    Range-based for loop without `items()` function:\n\n    @code{cpp}\n    for (auto it : j_object)\n    {\n        // \"it\" is of type json::reference and has no key() member\n        std::cout << \"value: \" << it << '\\n';\n    }\n    @endcode\n\n    Range-based for loop with `items()` function:\n\n    @code{cpp}\n    for (auto it : j_object.items())\n    {\n        std::cout << \"key: \" << it.key() << \", value:\" << it.value() << '\\n';\n    }\n    @endcode\n\n    @note When iterating over an array, `key()` will return the index of the\n          element as string (see example). For primitive types (e.g., numbers),\n          `key()` returns an empty string.\n\n    @return iteration proxy object wrapping @a ref with an interface to use in\n            range-based for loops\n\n    @liveexample{The following code shows how the function is used.,items}\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 3.1.0.\n    */\n    iteration_proxy<iterator> items() noexcept\n    {\n        return iteration_proxy<iterator>(*this);\n    }\n\n    /*!\n    @copydoc items()\n    */\n    iteration_proxy<const_iterator> items() const noexcept\n    {\n        return iteration_proxy<const_iterator>(*this);\n    }\n\n    /// @}\n\n\n    //////////////\n    // capacity //\n    //////////////\n\n    /// @name capacity\n    /// @{\n\n    /*!\n    @brief checks whether the container is empty.\n\n    Checks if a JSON value has no elements (i.e. whether its @ref size is `0`).\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `true`\n            boolean     | `false`\n            string      | `false`\n            number      | `false`\n            object      | result of function `object_t::empty()`\n            array       | result of function `array_t::empty()`\n\n    @liveexample{The following code uses `empty()` to check if a JSON\n    object contains any elements.,empty}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their `empty()` functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @note This function does not return whether a string stored as JSON value\n    is empty - it returns whether the JSON container itself is empty which is\n    false in the case of a string.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `begin() == end()`.\n\n    @sa @ref size() -- returns the number of elements\n\n    @since version 1.0.0\n    */\n    bool empty() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return true;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::empty()\n                return m_value.array->empty();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::empty()\n                return m_value.object->empty();\n            }\n\n            default:\n            {\n                // all other types are nonempty\n                return false;\n            }\n        }\n    }\n\n    /*!\n    @brief returns the number of elements\n\n    Returns the number of elements in a JSON value.\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `0`\n            boolean     | `1`\n            string      | `1`\n            number      | `1`\n            object      | result of function object_t::size()\n            array       | result of function array_t::size()\n\n    @liveexample{The following code calls `size()` on the different value\n    types.,size}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their size() functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @note This function does not return the length of a string stored as JSON\n    value - it returns the number of elements in the JSON value which is 1 in\n    the case of a string.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of `std::distance(begin(), end())`.\n\n    @sa @ref empty() -- checks whether the container is empty\n    @sa @ref max_size() -- returns the maximal number of elements\n\n    @since version 1.0.0\n    */\n    size_type size() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::null:\n            {\n                // null values are empty\n                return 0;\n            }\n\n            case value_t::array:\n            {\n                // delegate call to array_t::size()\n                return m_value.array->size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::size()\n                return m_value.object->size();\n            }\n\n            default:\n            {\n                // all other types have size 1\n                return 1;\n            }\n        }\n    }\n\n    /*!\n    @brief returns the maximum possible number of elements\n\n    Returns the maximum number of elements a JSON value is able to hold due to\n    system or library implementation limitations, i.e. `std::distance(begin(),\n    end())` for the JSON value.\n\n    @return The return value depends on the different types and is\n            defined as follows:\n            Value type  | return value\n            ----------- | -------------\n            null        | `0` (same as `size()`)\n            boolean     | `1` (same as `size()`)\n            string      | `1` (same as `size()`)\n            number      | `1` (same as `size()`)\n            object      | result of function `object_t::max_size()`\n            array       | result of function `array_t::max_size()`\n\n    @liveexample{The following code calls `max_size()` on the different value\n    types. Note the output is implementation specific.,max_size}\n\n    @complexity Constant, as long as @ref array_t and @ref object_t satisfy\n    the Container concept; that is, their `max_size()` functions have constant\n    complexity.\n\n    @iterators No changes.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @requirement This function helps `basic_json` satisfying the\n    [Container](https://en.cppreference.com/w/cpp/named_req/Container)\n    requirements:\n    - The complexity is constant.\n    - Has the semantics of returning `b.size()` where `b` is the largest\n      possible JSON value.\n\n    @sa @ref size() -- returns the number of elements\n\n    @since version 1.0.0\n    */\n    size_type max_size() const noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::array:\n            {\n                // delegate call to array_t::max_size()\n                return m_value.array->max_size();\n            }\n\n            case value_t::object:\n            {\n                // delegate call to object_t::max_size()\n                return m_value.object->max_size();\n            }\n\n            default:\n            {\n                // all other types have max_size() == size()\n                return size();\n            }\n        }\n    }\n\n    /// @}\n\n\n    ///////////////\n    // modifiers //\n    ///////////////\n\n    /// @name modifiers\n    /// @{\n\n    /*!\n    @brief clears the contents\n\n    Clears the content of a JSON value and resets it to the default value as\n    if @ref basic_json(value_t) would have been called with the current value\n    type from @ref type():\n\n    Value type  | initial value\n    ----------- | -------------\n    null        | `null`\n    boolean     | `false`\n    string      | `\"\"`\n    number      | `0`\n    object      | `{}`\n    array       | `[]`\n\n    @post Has the same effect as calling\n    @code {.cpp}\n    *this = basic_json(type());\n    @endcode\n\n    @liveexample{The example below shows the effect of `clear()` to different\n    JSON types.,clear}\n\n    @complexity Linear in the size of the JSON value.\n\n    @iterators All iterators, pointers and references related to this container\n               are invalidated.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @sa @ref basic_json(value_t) -- constructor that creates an object with the\n        same value than calling `clear()`\n\n    @since version 1.0.0\n    */\n    void clear() noexcept\n    {\n        switch (m_type)\n        {\n            case value_t::number_integer:\n            {\n                m_value.number_integer = 0;\n                break;\n            }\n\n            case value_t::number_unsigned:\n            {\n                m_value.number_unsigned = 0;\n                break;\n            }\n\n            case value_t::number_float:\n            {\n                m_value.number_float = 0.0;\n                break;\n            }\n\n            case value_t::boolean:\n            {\n                m_value.boolean = false;\n                break;\n            }\n\n            case value_t::string:\n            {\n                m_value.string->clear();\n                break;\n            }\n\n            case value_t::array:\n            {\n                m_value.array->clear();\n                break;\n            }\n\n            case value_t::object:\n            {\n                m_value.object->clear();\n                break;\n            }\n\n            default:\n                break;\n        }\n    }\n\n    /*!\n    @brief add an object to an array\n\n    Appends the given element @a val to the end of the JSON value. If the\n    function is called on a JSON null value, an empty array is created before\n    appending @a val.\n\n    @param[in] val the value to add to the JSON array\n\n    @throw type_error.308 when called on a type other than JSON array or\n    null; example: `\"cannot use push_back() with number\"`\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows how `push_back()` and `+=` can be used to\n    add elements to a JSON array. Note how the `null` value was silently\n    converted to a JSON array.,push_back}\n\n    @since version 1.0.0\n    */\n    void push_back(basic_json&& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_UNLIKELY(not(is_null() or is_array())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array (move semantics)\n        m_value.array->push_back(std::move(val));\n        // invalidate object\n        val.m_type = value_t::null;\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    reference operator+=(basic_json&& val)\n    {\n        push_back(std::move(val));\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    void push_back(const basic_json& val)\n    {\n        // push_back only works for null objects or arrays\n        if (JSON_UNLIKELY(not(is_null() or is_array())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array\n        m_value.array->push_back(val);\n    }\n\n    /*!\n    @brief add an object to an array\n    @copydoc push_back(basic_json&&)\n    */\n    reference operator+=(const basic_json& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an object\n\n    Inserts the given element @a val to the JSON object. If the function is\n    called on a JSON null value, an empty object is created before inserting\n    @a val.\n\n    @param[in] val the value to add to the JSON object\n\n    @throw type_error.308 when called on a type other than JSON object or\n    null; example: `\"cannot use push_back() with number\"`\n\n    @complexity Logarithmic in the size of the container, O(log(`size()`)).\n\n    @liveexample{The example shows how `push_back()` and `+=` can be used to\n    add elements to a JSON object. Note how the `null` value was silently\n    converted to a JSON object.,push_back__object_t__value}\n\n    @since version 1.0.0\n    */\n    void push_back(const typename object_t::value_type& val)\n    {\n        // push_back only works for null objects or objects\n        if (JSON_UNLIKELY(not(is_null() or is_object())))\n        {\n            JSON_THROW(type_error::create(308, \"cannot use push_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add element to array\n        m_value.object->insert(val);\n    }\n\n    /*!\n    @brief add an object to an object\n    @copydoc push_back(const typename object_t::value_type&)\n    */\n    reference operator+=(const typename object_t::value_type& val)\n    {\n        push_back(val);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an object\n\n    This function allows to use `push_back` with an initializer list. In case\n\n    1. the current value is an object,\n    2. the initializer list @a init contains only two elements, and\n    3. the first element of @a init is a string,\n\n    @a init is converted into an object element and added using\n    @ref push_back(const typename object_t::value_type&). Otherwise, @a init\n    is converted to a JSON value and added using @ref push_back(basic_json&&).\n\n    @param[in] init  an initializer list\n\n    @complexity Linear in the size of the initializer list @a init.\n\n    @note This function is required to resolve an ambiguous overload error,\n          because pairs like `{\"key\", \"value\"}` can be both interpreted as\n          `object_t::value_type` or `std::initializer_list<basic_json>`, see\n          https://github.com/nlohmann/json/issues/235 for more information.\n\n    @liveexample{The example shows how initializer lists are treated as\n    objects when possible.,push_back__initializer_list}\n    */\n    void push_back(initializer_list_t init)\n    {\n        if (is_object() and init.size() == 2 and (*init.begin())->is_string())\n        {\n            basic_json&& key = init.begin()->moved_or_copied();\n            push_back(typename object_t::value_type(\n                          std::move(key.get_ref<string_t&>()), (init.begin() + 1)->moved_or_copied()));\n        }\n        else\n        {\n            push_back(basic_json(init));\n        }\n    }\n\n    /*!\n    @brief add an object to an object\n    @copydoc push_back(initializer_list_t)\n    */\n    reference operator+=(initializer_list_t init)\n    {\n        push_back(init);\n        return *this;\n    }\n\n    /*!\n    @brief add an object to an array\n\n    Creates a JSON value from the passed parameters @a args to the end of the\n    JSON value. If the function is called on a JSON null value, an empty array\n    is created before appending the value created from @a args.\n\n    @param[in] args arguments to forward to a constructor of @ref basic_json\n    @tparam Args compatible types to create a @ref basic_json object\n\n    @throw type_error.311 when called on a type other than JSON array or\n    null; example: `\"cannot use emplace_back() with number\"`\n\n    @complexity Amortized constant.\n\n    @liveexample{The example shows how `push_back()` can be used to add\n    elements to a JSON array. Note how the `null` value was silently converted\n    to a JSON array.,emplace_back}\n\n    @since version 2.0.8\n    */\n    template<class... Args>\n    void emplace_back(Args&& ... args)\n    {\n        // emplace_back only works for null objects or arrays\n        if (JSON_UNLIKELY(not(is_null() or is_array())))\n        {\n            JSON_THROW(type_error::create(311, \"cannot use emplace_back() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an array\n        if (is_null())\n        {\n            m_type = value_t::array;\n            m_value = value_t::array;\n            assert_invariant();\n        }\n\n        // add element to array (perfect forwarding)\n        m_value.array->emplace_back(std::forward<Args>(args)...);\n    }\n\n    /*!\n    @brief add an object to an object if key does not exist\n\n    Inserts a new element into a JSON object constructed in-place with the\n    given @a args if there is no element with the key in the container. If the\n    function is called on a JSON null value, an empty object is created before\n    appending the value created from @a args.\n\n    @param[in] args arguments to forward to a constructor of @ref basic_json\n    @tparam Args compatible types to create a @ref basic_json object\n\n    @return a pair consisting of an iterator to the inserted element, or the\n            already-existing element if no insertion happened, and a bool\n            denoting whether the insertion took place.\n\n    @throw type_error.311 when called on a type other than JSON object or\n    null; example: `\"cannot use emplace() with number\"`\n\n    @complexity Logarithmic in the size of the container, O(log(`size()`)).\n\n    @liveexample{The example shows how `emplace()` can be used to add elements\n    to a JSON object. Note how the `null` value was silently converted to a\n    JSON object. Further note how no value is added if there was already one\n    value stored with the same key.,emplace}\n\n    @since version 2.0.8\n    */\n    template<class... Args>\n    std::pair<iterator, bool> emplace(Args&& ... args)\n    {\n        // emplace only works for null objects or arrays\n        if (JSON_UNLIKELY(not(is_null() or is_object())))\n        {\n            JSON_THROW(type_error::create(311, \"cannot use emplace() with \" + std::string(type_name())));\n        }\n\n        // transform null object into an object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value = value_t::object;\n            assert_invariant();\n        }\n\n        // add element to array (perfect forwarding)\n        auto res = m_value.object->emplace(std::forward<Args>(args)...);\n        // create result iterator and set iterator to the result of emplace\n        auto it = begin();\n        it.m_it.object_iterator = res.first;\n\n        // return pair of iterator and boolean\n        return {it, res.second};\n    }\n\n    /// Helper for insertion of an iterator\n    /// @note: This uses std::distance to support GCC 4.8,\n    ///        see https://github.com/nlohmann/json/pull/1257\n    template<typename... Args>\n    iterator insert_iterator(const_iterator pos, Args&& ... args)\n    {\n        iterator result(this);\n        assert(m_value.array != nullptr);\n\n        auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator);\n        m_value.array->insert(pos.m_it.array_iterator, std::forward<Args>(args)...);\n        result.m_it.array_iterator = m_value.array->begin() + insert_pos;\n\n        // This could have been written as:\n        // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val);\n        // but the return value of insert is missing in GCC 4.8, so it is written this way instead.\n\n        return result;\n    }\n\n    /*!\n    @brief inserts element\n\n    Inserts element @a val before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] val element to insert\n    @return iterator pointing to the inserted @a val.\n\n    @throw type_error.309 if called on JSON values other than arrays;\n    example: `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @complexity Constant plus linear in the distance between @a pos and end of\n    the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, const basic_json& val)\n    {\n        // insert only works for arrays\n        if (JSON_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, val);\n        }\n\n        JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief inserts element\n    @copydoc insert(const_iterator, const basic_json&)\n    */\n    iterator insert(const_iterator pos, basic_json&& val)\n    {\n        return insert(pos, val);\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts @a cnt copies of @a val before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] cnt number of copies of @a val to insert\n    @param[in] val element to insert\n    @return iterator pointing to the first element inserted, or @a pos if\n    `cnt==0`\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @complexity Linear in @a cnt plus linear in the distance between @a pos\n    and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__count}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, size_type cnt, const basic_json& val)\n    {\n        // insert only works for arrays\n        if (JSON_LIKELY(is_array()))\n        {\n            // check if iterator pos fits to this JSON value\n            if (JSON_UNLIKELY(pos.m_object != this))\n            {\n                JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n            }\n\n            // insert to array and return iterator\n            return insert_iterator(pos, cnt, val);\n        }\n\n        JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from range `[first, last)` before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n    @throw invalid_iterator.211 if @a first or @a last are iterators into\n    container for which insert is called; example: `\"passed iterators may not\n    belong to container\"`\n\n    @return iterator pointing to the first element inserted, or @a pos if\n    `first==last`\n\n    @complexity Linear in `std::distance(first, last)` plus linear in the\n    distance between @a pos and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__range}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, const_iterator first, const_iterator last)\n    {\n        // insert only works for arrays\n        if (JSON_UNLIKELY(not is_array()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\"));\n        }\n\n        if (JSON_UNLIKELY(first.m_object == this))\n        {\n            JSON_THROW(invalid_iterator::create(211, \"passed iterators may not belong to container\"));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from initializer list @a ilist before iterator @a pos.\n\n    @param[in] pos iterator before which the content will be inserted; may be\n    the end() iterator\n    @param[in] ilist initializer list to insert the values from\n\n    @throw type_error.309 if called on JSON values other than arrays; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if @a pos is not an iterator of *this;\n    example: `\"iterator does not fit current value\"`\n\n    @return iterator pointing to the first element inserted, or @a pos if\n    `ilist` is empty\n\n    @complexity Linear in `ilist.size()` plus linear in the distance between\n    @a pos and end of the container.\n\n    @liveexample{The example shows how `insert()` is used.,insert__ilist}\n\n    @since version 1.0.0\n    */\n    iterator insert(const_iterator pos, initializer_list_t ilist)\n    {\n        // insert only works for arrays\n        if (JSON_UNLIKELY(not is_array()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n        }\n\n        // check if iterator pos fits to this JSON value\n        if (JSON_UNLIKELY(pos.m_object != this))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterator does not fit current value\"));\n        }\n\n        // insert to array and return iterator\n        return insert_iterator(pos, ilist.begin(), ilist.end());\n    }\n\n    /*!\n    @brief inserts elements\n\n    Inserts elements from range `[first, last)`.\n\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.309 if called on JSON values other than objects; example:\n    `\"cannot use insert() with string\"`\n    @throw invalid_iterator.202 if iterator @a first or @a last does does not\n    point to an object; example: `\"iterators first and last must point to\n    objects\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n\n    @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number\n    of elements to insert.\n\n    @liveexample{The example shows how `insert()` is used.,insert__range_object}\n\n    @since version 3.0.0\n    */\n    void insert(const_iterator first, const_iterator last)\n    {\n        // insert only works for objects\n        if (JSON_UNLIKELY(not is_object()))\n        {\n            JSON_THROW(type_error::create(309, \"cannot use insert() with \" + std::string(type_name())));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\"));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_UNLIKELY(not first.m_object->is_object()))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterators first and last must point to objects\"));\n        }\n\n        m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);\n    }\n\n    /*!\n    @brief updates a JSON object from another object, overwriting existing keys\n\n    Inserts all values from JSON object @a j and overwrites existing keys.\n\n    @param[in] j  JSON object to read values from\n\n    @throw type_error.312 if called on JSON values other than objects; example:\n    `\"cannot use update() with string\"`\n\n    @complexity O(N*log(size() + N)), where N is the number of elements to\n                insert.\n\n    @liveexample{The example shows how `update()` is used.,update}\n\n    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update\n\n    @since version 3.0.0\n    */\n    void update(const_reference j)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        if (JSON_UNLIKELY(not is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(type_name())));\n        }\n        if (JSON_UNLIKELY(not j.is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(j.type_name())));\n        }\n\n        for (auto it = j.cbegin(); it != j.cend(); ++it)\n        {\n            m_value.object->operator[](it.key()) = it.value();\n        }\n    }\n\n    /*!\n    @brief updates a JSON object from another object, overwriting existing keys\n\n    Inserts all values from from range `[first, last)` and overwrites existing\n    keys.\n\n    @param[in] first begin of the range of elements to insert\n    @param[in] last end of the range of elements to insert\n\n    @throw type_error.312 if called on JSON values other than objects; example:\n    `\"cannot use update() with string\"`\n    @throw invalid_iterator.202 if iterator @a first or @a last does does not\n    point to an object; example: `\"iterators first and last must point to\n    objects\"`\n    @throw invalid_iterator.210 if @a first and @a last do not belong to the\n    same JSON value; example: `\"iterators do not fit\"`\n\n    @complexity O(N*log(size() + N)), where N is the number of elements to\n                insert.\n\n    @liveexample{The example shows how `update()` is used__range.,update}\n\n    @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update\n\n    @since version 3.0.0\n    */\n    void update(const_iterator first, const_iterator last)\n    {\n        // implicitly convert null value to an empty object\n        if (is_null())\n        {\n            m_type = value_t::object;\n            m_value.object = create<object_t>();\n            assert_invariant();\n        }\n\n        if (JSON_UNLIKELY(not is_object()))\n        {\n            JSON_THROW(type_error::create(312, \"cannot use update() with \" + std::string(type_name())));\n        }\n\n        // check if range iterators belong to the same JSON object\n        if (JSON_UNLIKELY(first.m_object != last.m_object))\n        {\n            JSON_THROW(invalid_iterator::create(210, \"iterators do not fit\"));\n        }\n\n        // passed iterators must belong to objects\n        if (JSON_UNLIKELY(not first.m_object->is_object()\n                          or not last.m_object->is_object()))\n        {\n            JSON_THROW(invalid_iterator::create(202, \"iterators first and last must point to objects\"));\n        }\n\n        for (auto it = first; it != last; ++it)\n        {\n            m_value.object->operator[](it.key()) = it.value();\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of the JSON value with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other JSON value to exchange the contents with\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how JSON values can be swapped with\n    `swap()`.,swap__reference}\n\n    @since version 1.0.0\n    */\n    void swap(reference other) noexcept (\n        std::is_nothrow_move_constructible<value_t>::value and\n        std::is_nothrow_move_assignable<value_t>::value and\n        std::is_nothrow_move_constructible<json_value>::value and\n        std::is_nothrow_move_assignable<json_value>::value\n    )\n    {\n        std::swap(m_type, other.m_type);\n        std::swap(m_value, other.m_value);\n        assert_invariant();\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON array with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other array to exchange the contents with\n\n    @throw type_error.310 when JSON value is not an array; example: `\"cannot\n    use swap() with string\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how arrays can be swapped with\n    `swap()`.,swap__array_t}\n\n    @since version 1.0.0\n    */\n    void swap(array_t& other)\n    {\n        // swap only works for arrays\n        if (JSON_LIKELY(is_array()))\n        {\n            std::swap(*(m_value.array), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON object with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other object to exchange the contents with\n\n    @throw type_error.310 when JSON value is not an object; example:\n    `\"cannot use swap() with string\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how objects can be swapped with\n    `swap()`.,swap__object_t}\n\n    @since version 1.0.0\n    */\n    void swap(object_t& other)\n    {\n        // swap only works for objects\n        if (JSON_LIKELY(is_object()))\n        {\n            std::swap(*(m_value.object), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /*!\n    @brief exchanges the values\n\n    Exchanges the contents of a JSON string with those of @a other. Does not\n    invoke any move, copy, or swap operations on individual elements. All\n    iterators and references remain valid. The past-the-end iterator is\n    invalidated.\n\n    @param[in,out] other string to exchange the contents with\n\n    @throw type_error.310 when JSON value is not a string; example: `\"cannot\n    use swap() with boolean\"`\n\n    @complexity Constant.\n\n    @liveexample{The example below shows how strings can be swapped with\n    `swap()`.,swap__string_t}\n\n    @since version 1.0.0\n    */\n    void swap(string_t& other)\n    {\n        // swap only works for strings\n        if (JSON_LIKELY(is_string()))\n        {\n            std::swap(*(m_value.string), other);\n        }\n        else\n        {\n            JSON_THROW(type_error::create(310, \"cannot use swap() with \" + std::string(type_name())));\n        }\n    }\n\n    /// @}\n\n  public:\n    //////////////////////////////////////////\n    // lexicographical comparison operators //\n    //////////////////////////////////////////\n\n    /// @name lexicographical comparison operators\n    /// @{\n\n    /*!\n    @brief comparison: equal\n\n    Compares two JSON values for equality according to the following rules:\n    - Two JSON values are equal if (1) they are from the same type and (2)\n      their stored values are the same according to their respective\n      `operator==`.\n    - Integer and floating-point numbers are automatically converted before\n      comparison. Note than two NaN values are always treated as unequal.\n    - Two JSON null values are equal.\n\n    @note Floating-point inside JSON values numbers are compared with\n    `json::number_float_t::operator==` which is `double::operator==` by\n    default. To compare floating-point while respecting an epsilon, an alternative\n    [comparison function](https://github.com/mariokonrad/marnav/blob/master/src/marnav/math/floatingpoint.hpp#L34-#L39)\n    could be used, for instance\n    @code {.cpp}\n    template<typename T, typename = typename std::enable_if<std::is_floating_point<T>::value, T>::type>\n    inline bool is_same(T a, T b, T epsilon = std::numeric_limits<T>::epsilon()) noexcept\n    {\n        return std::abs(a - b) <= epsilon;\n    }\n    @endcode\n\n    @note NaN values never compare equal to themselves or to other NaN values.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether the values @a lhs and @a rhs are equal\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @complexity Linear.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__equal}\n\n    @since version 1.0.0\n    */\n    friend bool operator==(const_reference lhs, const_reference rhs) noexcept\n    {\n        const auto lhs_type = lhs.type();\n        const auto rhs_type = rhs.type();\n\n        if (lhs_type == rhs_type)\n        {\n            switch (lhs_type)\n            {\n                case value_t::array:\n                    return (*lhs.m_value.array == *rhs.m_value.array);\n\n                case value_t::object:\n                    return (*lhs.m_value.object == *rhs.m_value.object);\n\n                case value_t::null:\n                    return true;\n\n                case value_t::string:\n                    return (*lhs.m_value.string == *rhs.m_value.string);\n\n                case value_t::boolean:\n                    return (lhs.m_value.boolean == rhs.m_value.boolean);\n\n                case value_t::number_integer:\n                    return (lhs.m_value.number_integer == rhs.m_value.number_integer);\n\n                case value_t::number_unsigned:\n                    return (lhs.m_value.number_unsigned == rhs.m_value.number_unsigned);\n\n                case value_t::number_float:\n                    return (lhs.m_value.number_float == rhs.m_value.number_float);\n\n                default:\n                    return false;\n            }\n        }\n        else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float)\n        {\n            return (static_cast<number_float_t>(lhs.m_value.number_integer) == rhs.m_value.number_float);\n        }\n        else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer)\n        {\n            return (lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_integer));\n        }\n        else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float)\n        {\n            return (static_cast<number_float_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_float);\n        }\n        else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned)\n        {\n            return (lhs.m_value.number_float == static_cast<number_float_t>(rhs.m_value.number_unsigned));\n        }\n        else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer)\n        {\n            return (static_cast<number_integer_t>(lhs.m_value.number_unsigned) == rhs.m_value.number_integer);\n        }\n        else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned)\n        {\n            return (lhs.m_value.number_integer == static_cast<number_integer_t>(rhs.m_value.number_unsigned));\n        }\n\n        return false;\n    }\n\n    /*!\n    @brief comparison: equal\n    @copydoc operator==(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return (lhs == basic_json(rhs));\n    }\n\n    /*!\n    @brief comparison: equal\n    @copydoc operator==(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return (basic_json(lhs) == rhs);\n    }\n\n    /*!\n    @brief comparison: not equal\n\n    Compares two JSON values for inequality by calculating `not (lhs == rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether the values @a lhs and @a rhs are not equal\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__notequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator!=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return not (lhs == rhs);\n    }\n\n    /*!\n    @brief comparison: not equal\n    @copydoc operator!=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return (lhs != basic_json(rhs));\n    }\n\n    /*!\n    @brief comparison: not equal\n    @copydoc operator!=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return (basic_json(lhs) != rhs);\n    }\n\n    /*!\n    @brief comparison: less than\n\n    Compares whether one JSON value @a lhs is less than another JSON value @a\n    rhs according to the following rules:\n    - If @a lhs and @a rhs have the same type, the values are compared using\n      the default `<` operator.\n    - Integer and floating-point numbers are automatically converted before\n      comparison\n    - In case @a lhs and @a rhs have different types, the values are ignored\n      and the order of the types is considered, see\n      @ref operator<(const value_t, const value_t).\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is less than @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__less}\n\n    @since version 1.0.0\n    */\n    friend bool operator<(const_reference lhs, const_reference rhs) noexcept\n    {\n        const auto lhs_type = lhs.type();\n        const auto rhs_type = rhs.type();\n\n        if (lhs_type == rhs_type)\n        {\n            switch (lhs_type)\n            {\n                case value_t::array:\n                    return (*lhs.m_value.array) < (*rhs.m_value.array);\n\n                case value_t::object:\n                    return *lhs.m_value.object < *rhs.m_value.object;\n\n                case value_t::null:\n                    return false;\n\n                case value_t::string:\n                    return *lhs.m_value.string < *rhs.m_value.string;\n\n                case value_t::boolean:\n                    return lhs.m_value.boolean < rhs.m_value.boolean;\n\n                case value_t::number_integer:\n                    return lhs.m_value.number_integer < rhs.m_value.number_integer;\n\n                case value_t::number_unsigned:\n                    return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned;\n\n                case value_t::number_float:\n                    return lhs.m_value.number_float < rhs.m_value.number_float;\n\n                default:\n                    return false;\n            }\n        }\n        else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_integer) < rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer)\n        {\n            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_integer);\n        }\n        else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float)\n        {\n            return static_cast<number_float_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_float;\n        }\n        else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_float < static_cast<number_float_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned)\n        {\n            return lhs.m_value.number_integer < static_cast<number_integer_t>(rhs.m_value.number_unsigned);\n        }\n        else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer)\n        {\n            return static_cast<number_integer_t>(lhs.m_value.number_unsigned) < rhs.m_value.number_integer;\n        }\n\n        // We only reach this line if we cannot compare values. In that case,\n        // we compare types. Note we have to call the operator explicitly,\n        // because MSVC has problems otherwise.\n        return operator<(lhs_type, rhs_type);\n    }\n\n    /*!\n    @brief comparison: less than\n    @copydoc operator<(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return (lhs < basic_json(rhs));\n    }\n\n    /*!\n    @brief comparison: less than\n    @copydoc operator<(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return (basic_json(lhs) < rhs);\n    }\n\n    /*!\n    @brief comparison: less than or equal\n\n    Compares whether one JSON value @a lhs is less than or equal to another\n    JSON value by calculating `not (rhs < lhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is less than or equal to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__greater}\n\n    @since version 1.0.0\n    */\n    friend bool operator<=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return not (rhs < lhs);\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @copydoc operator<=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return (lhs <= basic_json(rhs));\n    }\n\n    /*!\n    @brief comparison: less than or equal\n    @copydoc operator<=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return (basic_json(lhs) <= rhs);\n    }\n\n    /*!\n    @brief comparison: greater than\n\n    Compares whether one JSON value @a lhs is greater than another\n    JSON value by calculating `not (lhs <= rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is greater than to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__lessequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator>(const_reference lhs, const_reference rhs) noexcept\n    {\n        return not (lhs <= rhs);\n    }\n\n    /*!\n    @brief comparison: greater than\n    @copydoc operator>(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return (lhs > basic_json(rhs));\n    }\n\n    /*!\n    @brief comparison: greater than\n    @copydoc operator>(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return (basic_json(lhs) > rhs);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n\n    Compares whether one JSON value @a lhs is greater than or equal to another\n    JSON value by calculating `not (lhs < rhs)`.\n\n    @param[in] lhs  first JSON value to consider\n    @param[in] rhs  second JSON value to consider\n    @return whether @a lhs is greater than or equal to @a rhs\n\n    @complexity Linear.\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @liveexample{The example demonstrates comparing several JSON\n    types.,operator__greaterequal}\n\n    @since version 1.0.0\n    */\n    friend bool operator>=(const_reference lhs, const_reference rhs) noexcept\n    {\n        return not (lhs < rhs);\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @copydoc operator>=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept\n    {\n        return (lhs >= basic_json(rhs));\n    }\n\n    /*!\n    @brief comparison: greater than or equal\n    @copydoc operator>=(const_reference, const_reference)\n    */\n    template<typename ScalarType, typename std::enable_if<\n                 std::is_scalar<ScalarType>::value, int>::type = 0>\n    friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept\n    {\n        return (basic_json(lhs) >= rhs);\n    }\n\n    /// @}\n\n    ///////////////////\n    // serialization //\n    ///////////////////\n\n    /// @name serialization\n    /// @{\n\n    /*!\n    @brief serialize to stream\n\n    Serialize the given JSON value @a j to the output stream @a o. The JSON\n    value will be serialized using the @ref dump member function.\n\n    - The indentation of the output can be controlled with the member variable\n      `width` of the output stream @a o. For instance, using the manipulator\n      `std::setw(4)` on @a o sets the indentation level to `4` and the\n      serialization result is the same as calling `dump(4)`.\n\n    - The indentation character can be controlled with the member variable\n      `fill` of the output stream @a o. For instance, the manipulator\n      `std::setfill('\\\\t')` sets indentation to use a tab character rather than\n      the default space character.\n\n    @param[in,out] o  stream to serialize to\n    @param[in] j  JSON value to serialize\n\n    @return the stream @a o\n\n    @throw type_error.316 if a string stored inside the JSON value is not\n                          UTF-8 encoded\n\n    @complexity Linear.\n\n    @liveexample{The example below shows the serialization with different\n    parameters to `width` to adjust the indentation level.,operator_serialize}\n\n    @since version 1.0.0; indentation character added in version 3.0.0\n    */\n    friend std::ostream& operator<<(std::ostream& o, const basic_json& j)\n    {\n        // read width member and use it as indentation parameter if nonzero\n        const bool pretty_print = (o.width() > 0);\n        const auto indentation = (pretty_print ? o.width() : 0);\n\n        // reset width to 0 for subsequent calls to this stream\n        o.width(0);\n\n        // do the actual serialization\n        serializer s(detail::output_adapter<char>(o), o.fill());\n        s.dump(j, pretty_print, false, static_cast<unsigned int>(indentation));\n        return o;\n    }\n\n    /*!\n    @brief serialize to stream\n    @deprecated This stream operator is deprecated and will be removed in\n                future 4.0.0 of the library. Please use\n                @ref operator<<(std::ostream&, const basic_json&)\n                instead; that is, replace calls like `j >> o;` with `o << j;`.\n    @since version 1.0.0; deprecated since version 3.0.0\n    */\n    JSON_DEPRECATED\n    friend std::ostream& operator>>(const basic_json& j, std::ostream& o)\n    {\n        return o << j;\n    }\n\n    /// @}\n\n\n    /////////////////////\n    // deserialization //\n    /////////////////////\n\n    /// @name deserialization\n    /// @{\n\n    /*!\n    @brief deserialize from a compatible input\n\n    This function reads from a compatible input. Examples are:\n    - an array of 1-byte values\n    - strings with character/literal type with size of 1 byte\n    - input streams\n    - container with contiguous storage of 1-byte values. Compatible container\n      types include `std::vector`, `std::string`, `std::array`,\n      `std::valarray`, and `std::initializer_list`. Furthermore, C-style\n      arrays can be used with `std::begin()`/`std::end()`. User-defined\n      containers can be used as long as they implement random-access iterators\n      and a contiguous storage.\n\n    @pre Each element of the container has a size of 1 byte. Violating this\n    precondition yields undefined behavior. **This precondition is enforced\n    with a static assertion.**\n\n    @pre The container storage is contiguous. Violating this precondition\n    yields undefined behavior. **This precondition is enforced with an\n    assertion.**\n    @pre Each element of the container has a size of 1 byte. Violating this\n    precondition yields undefined behavior. **This precondition is enforced\n    with a static assertion.**\n\n    @warning There is no way to enforce all preconditions at compile-time. If\n             the function is called with a noncompliant container and with\n             assertions switched off, the behavior is undefined and will most\n             likely yield segmentation violation.\n\n    @param[in] i  input to read from\n    @param[in] cb  a parser callback function of type @ref parser_callback_t\n    which is used to control the deserialization by filtering unwanted values\n    (optional)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return result of the deserialization\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser. The complexity can be higher if the parser callback function\n    @a cb has a super-linear complexity.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `parse()` function reading\n    from an array.,parse__array__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function with\n    and without callback function.,parse__string__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function with\n    and without callback function.,parse__istream__parser_callback_t}\n\n    @liveexample{The example below demonstrates the `parse()` function reading\n    from a contiguous container.,parse__contiguouscontainer__parser_callback_t}\n\n    @since version 2.0.3 (contiguous containers)\n    */\n    static basic_json parse(detail::input_adapter&& i,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true)\n    {\n        basic_json result;\n        parser(i, cb, allow_exceptions).parse(true, result);\n        return result;\n    }\n\n    static bool accept(detail::input_adapter&& i)\n    {\n        return parser(i).accept(true);\n    }\n\n    /*!\n    @brief generate SAX events\n\n    The SAX event lister must follow the interface of @ref json_sax.\n\n    This function reads from a compatible input. Examples are:\n    - an array of 1-byte values\n    - strings with character/literal type with size of 1 byte\n    - input streams\n    - container with contiguous storage of 1-byte values. Compatible container\n      types include `std::vector`, `std::string`, `std::array`,\n      `std::valarray`, and `std::initializer_list`. Furthermore, C-style\n      arrays can be used with `std::begin()`/`std::end()`. User-defined\n      containers can be used as long as they implement random-access iterators\n      and a contiguous storage.\n\n    @pre Each element of the container has a size of 1 byte. Violating this\n    precondition yields undefined behavior. **This precondition is enforced\n    with a static assertion.**\n\n    @pre The container storage is contiguous. Violating this precondition\n    yields undefined behavior. **This precondition is enforced with an\n    assertion.**\n    @pre Each element of the container has a size of 1 byte. Violating this\n    precondition yields undefined behavior. **This precondition is enforced\n    with a static assertion.**\n\n    @warning There is no way to enforce all preconditions at compile-time. If\n             the function is called with a noncompliant container and with\n             assertions switched off, the behavior is undefined and will most\n             likely yield segmentation violation.\n\n    @param[in] i  input to read from\n    @param[in,out] sax  SAX event listener\n    @param[in] format  the format to parse (JSON, CBOR, MessagePack, or UBJSON)\n    @param[in] strict  whether the input has to be consumed completely\n\n    @return return value of the last processed SAX event\n\n    @throw parse_error.101 if a parse error occurs; example: `\"\"unexpected end\n    of input; expected string literal\"\"`\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser. The complexity can be higher if the SAX consumer @a sax has\n    a super-linear complexity.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `sax_parse()` function\n    reading from string and processing the events with a user-defined SAX\n    event consumer.,sax_parse}\n\n    @since version 3.2.0\n    */\n    template <typename SAX>\n    static bool sax_parse(detail::input_adapter&& i, SAX* sax,\n                          input_format_t format = input_format_t::json,\n                          const bool strict = true)\n    {\n        assert(sax);\n        switch (format)\n        {\n            case input_format_t::json:\n                return parser(std::move(i)).sax_parse(sax, strict);\n            default:\n                return detail::binary_reader<basic_json, SAX>(std::move(i)).sax_parse(format, sax, strict);\n        }\n    }\n\n    /*!\n    @brief deserialize from an iterator range with contiguous storage\n\n    This function reads from an iterator range of a container with contiguous\n    storage of 1-byte values. Compatible container types include\n    `std::vector`, `std::string`, `std::array`, `std::valarray`, and\n    `std::initializer_list`. Furthermore, C-style arrays can be used with\n    `std::begin()`/`std::end()`. User-defined containers can be used as long\n    as they implement random-access iterators and a contiguous storage.\n\n    @pre The iterator range is contiguous. Violating this precondition yields\n    undefined behavior. **This precondition is enforced with an assertion.**\n    @pre Each element in the range has a size of 1 byte. Violating this\n    precondition yields undefined behavior. **This precondition is enforced\n    with a static assertion.**\n\n    @warning There is no way to enforce all preconditions at compile-time. If\n             the function is called with noncompliant iterators and with\n             assertions switched off, the behavior is undefined and will most\n             likely yield segmentation violation.\n\n    @tparam IteratorType iterator of container with contiguous storage\n    @param[in] first  begin of the range to parse (included)\n    @param[in] last  end of the range to parse (excluded)\n    @param[in] cb  a parser callback function of type @ref parser_callback_t\n    which is used to control the deserialization by filtering unwanted values\n    (optional)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return result of the deserialization\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser. The complexity can be higher if the parser callback function\n    @a cb has a super-linear complexity.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below demonstrates the `parse()` function reading\n    from an iterator range.,parse__iteratortype__parser_callback_t}\n\n    @since version 2.0.3\n    */\n    template<class IteratorType, typename std::enable_if<\n                 std::is_base_of<\n                     std::random_access_iterator_tag,\n                     typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>\n    static basic_json parse(IteratorType first, IteratorType last,\n                            const parser_callback_t cb = nullptr,\n                            const bool allow_exceptions = true)\n    {\n        basic_json result;\n        parser(detail::input_adapter(first, last), cb, allow_exceptions).parse(true, result);\n        return result;\n    }\n\n    template<class IteratorType, typename std::enable_if<\n                 std::is_base_of<\n                     std::random_access_iterator_tag,\n                     typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>\n    static bool accept(IteratorType first, IteratorType last)\n    {\n        return parser(detail::input_adapter(first, last)).accept(true);\n    }\n\n    template<class IteratorType, class SAX, typename std::enable_if<\n                 std::is_base_of<\n                     std::random_access_iterator_tag,\n                     typename std::iterator_traits<IteratorType>::iterator_category>::value, int>::type = 0>\n    static bool sax_parse(IteratorType first, IteratorType last, SAX* sax)\n    {\n        return parser(detail::input_adapter(first, last)).sax_parse(sax);\n    }\n\n    /*!\n    @brief deserialize from stream\n    @deprecated This stream operator is deprecated and will be removed in\n                version 4.0.0 of the library. Please use\n                @ref operator>>(std::istream&, basic_json&)\n                instead; that is, replace calls like `j << i;` with `i >> j;`.\n    @since version 1.0.0; deprecated since version 3.0.0\n    */\n    JSON_DEPRECATED\n    friend std::istream& operator<<(basic_json& j, std::istream& i)\n    {\n        return operator>>(i, j);\n    }\n\n    /*!\n    @brief deserialize from stream\n\n    Deserializes an input stream to a JSON value.\n\n    @param[in,out] i  input stream to read a serialized JSON value from\n    @param[in,out] j  JSON value to write the deserialized input to\n\n    @throw parse_error.101 in case of an unexpected token\n    @throw parse_error.102 if to_unicode fails or surrogate error\n    @throw parse_error.103 if to_unicode fails\n\n    @complexity Linear in the length of the input. The parser is a predictive\n    LL(1) parser.\n\n    @note A UTF-8 byte order mark is silently ignored.\n\n    @liveexample{The example below shows how a JSON value is constructed by\n    reading a serialization from a stream.,operator_deserialize}\n\n    @sa parse(std::istream&, const parser_callback_t) for a variant with a\n    parser callback function to filter values while parsing\n\n    @since version 1.0.0\n    */\n    friend std::istream& operator>>(std::istream& i, basic_json& j)\n    {\n        parser(detail::input_adapter(i)).parse(false, j);\n        return i;\n    }\n\n    /// @}\n\n    ///////////////////////////\n    // convenience functions //\n    ///////////////////////////\n\n    /*!\n    @brief return the type as string\n\n    Returns the type name as string to be used in error messages - usually to\n    indicate that a function was called on a wrong JSON type.\n\n    @return a string representation of a the @a m_type member:\n            Value type  | return value\n            ----------- | -------------\n            null        | `\"null\"`\n            boolean     | `\"boolean\"`\n            string      | `\"string\"`\n            number      | `\"number\"` (for all number types)\n            object      | `\"object\"`\n            array       | `\"array\"`\n            discarded   | `\"discarded\"`\n\n    @exceptionsafety No-throw guarantee: this function never throws exceptions.\n\n    @complexity Constant.\n\n    @liveexample{The following code exemplifies `type_name()` for all JSON\n    types.,type_name}\n\n    @sa @ref type() -- return the type of the JSON value\n    @sa @ref operator value_t() -- return the type of the JSON value (implicit)\n\n    @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept`\n    since 3.0.0\n    */\n    const char* type_name() const noexcept\n    {\n        {\n            switch (m_type)\n            {\n                case value_t::null:\n                    return \"null\";\n                case value_t::object:\n                    return \"object\";\n                case value_t::array:\n                    return \"array\";\n                case value_t::string:\n                    return \"string\";\n                case value_t::boolean:\n                    return \"boolean\";\n                case value_t::discarded:\n                    return \"discarded\";\n                default:\n                    return \"number\";\n            }\n        }\n    }\n\n\n  private:\n    //////////////////////\n    // member variables //\n    //////////////////////\n\n    /// the type of the current element\n    value_t m_type = value_t::null;\n\n    /// the value of the current element\n    json_value m_value = {};\n\n    //////////////////////////////////////////\n    // binary serialization/deserialization //\n    //////////////////////////////////////////\n\n    /// @name binary serialization/deserialization support\n    /// @{\n\n  public:\n    /*!\n    @brief create a CBOR serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the CBOR (Concise\n    Binary Object Representation) serialization format. CBOR is a binary\n    serialization format which aims to be more compact than JSON itself, yet\n    more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    CBOR types according to the CBOR specification (RFC 7049):\n\n    JSON value type | value/range                                | CBOR type                          | first byte\n    --------------- | ------------------------------------------ | ---------------------------------- | ---------------\n    null            | `null`                                     | Null                               | 0xF6\n    boolean         | `true`                                     | True                               | 0xF5\n    boolean         | `false`                                    | False                              | 0xF4\n    number_integer  | -9223372036854775808..-2147483649          | Negative integer (8 bytes follow)  | 0x3B\n    number_integer  | -2147483648..-32769                        | Negative integer (4 bytes follow)  | 0x3A\n    number_integer  | -32768..-129                               | Negative integer (2 bytes follow)  | 0x39\n    number_integer  | -128..-25                                  | Negative integer (1 byte follow)   | 0x38\n    number_integer  | -24..-1                                    | Negative integer                   | 0x20..0x37\n    number_integer  | 0..23                                      | Integer                            | 0x00..0x17\n    number_integer  | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18\n    number_integer  | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19\n    number_integer  | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A\n    number_integer  | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B\n    number_unsigned | 0..23                                      | Integer                            | 0x00..0x17\n    number_unsigned | 24..255                                    | Unsigned integer (1 byte follow)   | 0x18\n    number_unsigned | 256..65535                                 | Unsigned integer (2 bytes follow)  | 0x19\n    number_unsigned | 65536..4294967295                          | Unsigned integer (4 bytes follow)  | 0x1A\n    number_unsigned | 4294967296..18446744073709551615           | Unsigned integer (8 bytes follow)  | 0x1B\n    number_float    | *any value*                                | Double-Precision Float             | 0xFB\n    string          | *length*: 0..23                            | UTF-8 string                       | 0x60..0x77\n    string          | *length*: 23..255                          | UTF-8 string (1 byte follow)       | 0x78\n    string          | *length*: 256..65535                       | UTF-8 string (2 bytes follow)      | 0x79\n    string          | *length*: 65536..4294967295                | UTF-8 string (4 bytes follow)      | 0x7A\n    string          | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow)      | 0x7B\n    array           | *size*: 0..23                              | array                              | 0x80..0x97\n    array           | *size*: 23..255                            | array (1 byte follow)              | 0x98\n    array           | *size*: 256..65535                         | array (2 bytes follow)             | 0x99\n    array           | *size*: 65536..4294967295                  | array (4 bytes follow)             | 0x9A\n    array           | *size*: 4294967296..18446744073709551615   | array (8 bytes follow)             | 0x9B\n    object          | *size*: 0..23                              | map                                | 0xA0..0xB7\n    object          | *size*: 23..255                            | map (1 byte follow)                | 0xB8\n    object          | *size*: 256..65535                         | map (2 bytes follow)               | 0xB9\n    object          | *size*: 65536..4294967295                  | map (4 bytes follow)               | 0xBA\n    object          | *size*: 4294967296..18446744073709551615   | map (8 bytes follow)               | 0xBB\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a CBOR value.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @note The following CBOR types are not used in the conversion:\n          - byte strings (0x40..0x5F)\n          - UTF-8 strings terminated by \"break\" (0x7F)\n          - arrays terminated by \"break\" (0x9F)\n          - maps terminated by \"break\" (0xBF)\n          - date/time (0xC0..0xC1)\n          - bignum (0xC2..0xC3)\n          - decimal fraction (0xC4)\n          - bigfloat (0xC5)\n          - tagged items (0xC6..0xD4, 0xD8..0xDB)\n          - expected conversions (0xD5..0xD7)\n          - simple values (0xE0..0xF3, 0xF8)\n          - undefined (0xF7)\n          - half and single-precision floats (0xF9-0xFA)\n          - break (0xFF)\n\n    @param[in] j  JSON value to serialize\n    @return MessagePack serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in CBOR format.,to_cbor}\n\n    @sa http://cbor.io\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the\n        analogous deserialization\n    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n\n    @since version 2.0.9\n    */\n    static std::vector<uint8_t> to_cbor(const basic_json& j)\n    {\n        std::vector<uint8_t> result;\n        to_cbor(j, result);\n        return result;\n    }\n\n    static void to_cbor(const basic_json& j, detail::output_adapter<uint8_t> o)\n    {\n        binary_writer<uint8_t>(o).write_cbor(j);\n    }\n\n    static void to_cbor(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_cbor(j);\n    }\n\n    /*!\n    @brief create a MessagePack serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the MessagePack\n    serialization format. MessagePack is a binary serialization format which\n    aims to be more compact than JSON itself, yet more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    MessagePack types according to the MessagePack specification:\n\n    JSON value type | value/range                       | MessagePack type | first byte\n    --------------- | --------------------------------- | ---------------- | ----------\n    null            | `null`                            | nil              | 0xC0\n    boolean         | `true`                            | true             | 0xC3\n    boolean         | `false`                           | false            | 0xC2\n    number_integer  | -9223372036854775808..-2147483649 | int64            | 0xD3\n    number_integer  | -2147483648..-32769               | int32            | 0xD2\n    number_integer  | -32768..-129                      | int16            | 0xD1\n    number_integer  | -128..-33                         | int8             | 0xD0\n    number_integer  | -32..-1                           | negative fixint  | 0xE0..0xFF\n    number_integer  | 0..127                            | positive fixint  | 0x00..0x7F\n    number_integer  | 128..255                          | uint 8           | 0xCC\n    number_integer  | 256..65535                        | uint 16          | 0xCD\n    number_integer  | 65536..4294967295                 | uint 32          | 0xCE\n    number_integer  | 4294967296..18446744073709551615  | uint 64          | 0xCF\n    number_unsigned | 0..127                            | positive fixint  | 0x00..0x7F\n    number_unsigned | 128..255                          | uint 8           | 0xCC\n    number_unsigned | 256..65535                        | uint 16          | 0xCD\n    number_unsigned | 65536..4294967295                 | uint 32          | 0xCE\n    number_unsigned | 4294967296..18446744073709551615  | uint 64          | 0xCF\n    number_float    | *any value*                       | float 64         | 0xCB\n    string          | *length*: 0..31                   | fixstr           | 0xA0..0xBF\n    string          | *length*: 32..255                 | str 8            | 0xD9\n    string          | *length*: 256..65535              | str 16           | 0xDA\n    string          | *length*: 65536..4294967295       | str 32           | 0xDB\n    array           | *size*: 0..15                     | fixarray         | 0x90..0x9F\n    array           | *size*: 16..65535                 | array 16         | 0xDC\n    array           | *size*: 65536..4294967295         | array 32         | 0xDD\n    object          | *size*: 0..15                     | fix map          | 0x80..0x8F\n    object          | *size*: 16..65535                 | map 16           | 0xDE\n    object          | *size*: 65536..4294967295         | map 32           | 0xDF\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a MessagePack value.\n\n    @note The following values can **not** be converted to a MessagePack value:\n          - strings with more than 4294967295 bytes\n          - arrays with more than 4294967295 elements\n          - objects with more than 4294967295 elements\n\n    @note The following MessagePack types are not used in the conversion:\n          - bin 8 - bin 32 (0xC4..0xC6)\n          - ext 8 - ext 32 (0xC7..0xC9)\n          - float 32 (0xCA)\n          - fixext 1 - fixext 16 (0xD4..0xD8)\n\n    @note Any MessagePack output created @ref to_msgpack can be successfully\n          parsed by @ref from_msgpack.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @param[in] j  JSON value to serialize\n    @return MessagePack serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in MessagePack format.,to_msgpack}\n\n    @sa http://msgpack.org\n    @sa @ref from_msgpack for the analogous deserialization\n    @sa @ref to_cbor(const basic_json& for the related CBOR format\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n\n    @since version 2.0.9\n    */\n    static std::vector<uint8_t> to_msgpack(const basic_json& j)\n    {\n        std::vector<uint8_t> result;\n        to_msgpack(j, result);\n        return result;\n    }\n\n    static void to_msgpack(const basic_json& j, detail::output_adapter<uint8_t> o)\n    {\n        binary_writer<uint8_t>(o).write_msgpack(j);\n    }\n\n    static void to_msgpack(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_msgpack(j);\n    }\n\n    /*!\n    @brief create a UBJSON serialization of a given JSON value\n\n    Serializes a given JSON value @a j to a byte vector using the UBJSON\n    (Universal Binary JSON) serialization format. UBJSON aims to be more compact\n    than JSON itself, yet more efficient to parse.\n\n    The library uses the following mapping from JSON values types to\n    UBJSON types according to the UBJSON specification:\n\n    JSON value type | value/range                       | UBJSON type | marker\n    --------------- | --------------------------------- | ----------- | ------\n    null            | `null`                            | null        | `Z`\n    boolean         | `true`                            | true        | `T`\n    boolean         | `false`                           | false       | `F`\n    number_integer  | -9223372036854775808..-2147483649 | int64       | `L`\n    number_integer  | -2147483648..-32769               | int32       | `l`\n    number_integer  | -32768..-129                      | int16       | `I`\n    number_integer  | -128..127                         | int8        | `i`\n    number_integer  | 128..255                          | uint8       | `U`\n    number_integer  | 256..32767                        | int16       | `I`\n    number_integer  | 32768..2147483647                 | int32       | `l`\n    number_integer  | 2147483648..9223372036854775807   | int64       | `L`\n    number_unsigned | 0..127                            | int8        | `i`\n    number_unsigned | 128..255                          | uint8       | `U`\n    number_unsigned | 256..32767                        | int16       | `I`\n    number_unsigned | 32768..2147483647                 | int32       | `l`\n    number_unsigned | 2147483648..9223372036854775807   | int64       | `L`\n    number_float    | *any value*                       | float64     | `D`\n    string          | *with shortest length indicator*  | string      | `S`\n    array           | *see notes on optimized format*   | array       | `[`\n    object          | *see notes on optimized format*   | map         | `{`\n\n    @note The mapping is **complete** in the sense that any JSON value type\n          can be converted to a UBJSON value.\n\n    @note The following values can **not** be converted to a UBJSON value:\n          - strings with more than 9223372036854775807 bytes (theoretical)\n          - unsigned integer numbers above 9223372036854775807\n\n    @note The following markers are not used in the conversion:\n          - `Z`: no-op values are not created.\n          - `C`: single-byte strings are serialized with `S` markers.\n\n    @note Any UBJSON output created @ref to_ubjson can be successfully parsed\n          by @ref from_ubjson.\n\n    @note If NaN or Infinity are stored inside a JSON number, they are\n          serialized properly. This behavior differs from the @ref dump()\n          function which serializes NaN or Infinity to `null`.\n\n    @note The optimized formats for containers are supported: Parameter\n          @a use_size adds size information to the beginning of a container and\n          removes the closing marker. Parameter @a use_type further checks\n          whether all elements of a container have the same type and adds the\n          type marker to the beginning of the container. The @a use_type\n          parameter must only be used together with @a use_size = true. Note\n          that @a use_size = true alone may result in larger representations -\n          the benefit of this parameter is that the receiving side is\n          immediately informed on the number of elements of the container.\n\n    @param[in] j  JSON value to serialize\n    @param[in] use_size  whether to add size annotations to container types\n    @param[in] use_type  whether to add type annotations to container types\n                         (must be combined with @a use_size = true)\n    @return UBJSON serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in UBJSON format.,to_ubjson}\n\n    @sa http://ubjson.org\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the\n        analogous deserialization\n    @sa @ref to_cbor(const basic_json& for the related CBOR format\n    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format\n\n    @since version 3.1.0\n    */\n    static std::vector<uint8_t> to_ubjson(const basic_json& j,\n                                          const bool use_size = false,\n                                          const bool use_type = false)\n    {\n        std::vector<uint8_t> result;\n        to_ubjson(j, result, use_size, use_type);\n        return result;\n    }\n\n    static void to_ubjson(const basic_json& j, detail::output_adapter<uint8_t> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<uint8_t>(o).write_ubjson(j, use_size, use_type);\n    }\n\n    static void to_ubjson(const basic_json& j, detail::output_adapter<char> o,\n                          const bool use_size = false, const bool use_type = false)\n    {\n        binary_writer<char>(o).write_ubjson(j, use_size, use_type);\n    }\n\n\n    /*!\n    @brief Serializes the given JSON object `j` to BSON and returns a vector\n           containing the corresponding BSON-representation.\n\n    BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are\n    stored as a single entity (a so-called document).\n\n    The library uses the following mapping from JSON values types to BSON types:\n\n    JSON value type | value/range                       | BSON type   | marker\n    --------------- | --------------------------------- | ----------- | ------\n    null            | `null`                            | null        | 0x0A\n    boolean         | `true`, `false`                   | boolean     | 0x08\n    number_integer  | -9223372036854775808..-2147483649 | int64       | 0x12\n    number_integer  | -2147483648..2147483647           | int32       | 0x10\n    number_integer  | 2147483648..9223372036854775807   | int64       | 0x12\n    number_unsigned | 0..2147483647                     | int32       | 0x10\n    number_unsigned | 2147483648..9223372036854775807   | int64       | 0x12\n    number_unsigned | 9223372036854775808..18446744073709551615| --   | --\n    number_float    | *any value*                       | double      | 0x01\n    string          | *any value*                       | string      | 0x02\n    array           | *any value*                       | document    | 0x04\n    object          | *any value*                       | document    | 0x03\n\n    @warning The mapping is **incomplete**, since only JSON-objects (and things\n    contained therein) can be serialized to BSON.\n    Also, integers larger than 9223372036854775807 cannot be serialized to BSON,\n    and the keys may not contain U+0000, since they are serialized a\n    zero-terminated c-strings.\n\n    @throw out_of_range.407  if `j.is_number_unsigned() && j.get<std::uint64_t>() > 9223372036854775807`\n    @throw out_of_range.409  if a key in `j` contains a NULL (U+0000)\n    @throw type_error.317    if `!j.is_object()`\n\n    @pre The input `j` is required to be an object: `j.is_object() == true`.\n\n    @note Any BSON output created via @ref to_bson can be successfully parsed\n          by @ref from_bson.\n\n    @param[in] j  JSON value to serialize\n    @return BSON serialization as byte vector\n\n    @complexity Linear in the size of the JSON value @a j.\n\n    @liveexample{The example shows the serialization of a JSON value to a byte\n    vector in BSON format.,to_bson}\n\n    @sa http://bsonspec.org/spec.html\n    @sa @ref from_bson(detail::input_adapter&&, const bool strict) for the\n        analogous deserialization\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             related UBJSON format\n    @sa @ref to_cbor(const basic_json&) for the related CBOR format\n    @sa @ref to_msgpack(const basic_json&) for the related MessagePack format\n    */\n    static std::vector<uint8_t> to_bson(const basic_json& j)\n    {\n        std::vector<uint8_t> result;\n        to_bson(j, result);\n        return result;\n    }\n\n    /*!\n    @brief Serializes the given JSON object `j` to BSON and forwards the\n           corresponding BSON-representation to the given output_adapter `o`.\n    @param j The JSON object to convert to BSON.\n    @param o The output adapter that receives the binary BSON representation.\n    @pre The input `j` shall be an object: `j.is_object() == true`\n    @sa @ref to_bson(const basic_json&)\n    */\n    static void to_bson(const basic_json& j, detail::output_adapter<uint8_t> o)\n    {\n        binary_writer<uint8_t>(o).write_bson(j);\n    }\n\n    /*!\n    @copydoc to_bson(const basic_json&, detail::output_adapter<uint8_t>)\n    */\n    static void to_bson(const basic_json& j, detail::output_adapter<char> o)\n    {\n        binary_writer<char>(o).write_bson(j);\n    }\n\n\n    /*!\n    @brief create a JSON value from an input in CBOR format\n\n    Deserializes a given input @a i to a JSON value using the CBOR (Concise\n    Binary Object Representation) serialization format.\n\n    The library maps CBOR types to JSON value types as follows:\n\n    CBOR type              | JSON value type | first byte\n    ---------------------- | --------------- | ----------\n    Integer                | number_unsigned | 0x00..0x17\n    Unsigned integer       | number_unsigned | 0x18\n    Unsigned integer       | number_unsigned | 0x19\n    Unsigned integer       | number_unsigned | 0x1A\n    Unsigned integer       | number_unsigned | 0x1B\n    Negative integer       | number_integer  | 0x20..0x37\n    Negative integer       | number_integer  | 0x38\n    Negative integer       | number_integer  | 0x39\n    Negative integer       | number_integer  | 0x3A\n    Negative integer       | number_integer  | 0x3B\n    Negative integer       | number_integer  | 0x40..0x57\n    UTF-8 string           | string          | 0x60..0x77\n    UTF-8 string           | string          | 0x78\n    UTF-8 string           | string          | 0x79\n    UTF-8 string           | string          | 0x7A\n    UTF-8 string           | string          | 0x7B\n    UTF-8 string           | string          | 0x7F\n    array                  | array           | 0x80..0x97\n    array                  | array           | 0x98\n    array                  | array           | 0x99\n    array                  | array           | 0x9A\n    array                  | array           | 0x9B\n    array                  | array           | 0x9F\n    map                    | object          | 0xA0..0xB7\n    map                    | object          | 0xB8\n    map                    | object          | 0xB9\n    map                    | object          | 0xBA\n    map                    | object          | 0xBB\n    map                    | object          | 0xBF\n    False                  | `false`         | 0xF4\n    True                   | `true`          | 0xF5\n    Null                   | `null`          | 0xF6\n    Half-Precision Float   | number_float    | 0xF9\n    Single-Precision Float | number_float    | 0xFA\n    Double-Precision Float | number_float    | 0xFB\n\n    @warning The mapping is **incomplete** in the sense that not all CBOR\n             types can be converted to a JSON value. The following CBOR types\n             are not supported and will yield parse errors (parse_error.112):\n             - byte strings (0x40..0x5F)\n             - date/time (0xC0..0xC1)\n             - bignum (0xC2..0xC3)\n             - decimal fraction (0xC4)\n             - bigfloat (0xC5)\n             - tagged items (0xC6..0xD4, 0xD8..0xDB)\n             - expected conversions (0xD5..0xD7)\n             - simple values (0xE0..0xF3, 0xF8)\n             - undefined (0xF7)\n\n    @warning CBOR allows map keys of any type, whereas JSON only allows\n             strings as keys in object values. Therefore, CBOR maps with keys\n             other than UTF-8 strings are rejected (parse_error.113).\n\n    @note Any CBOR output created @ref to_cbor can be successfully parsed by\n          @ref from_cbor.\n\n    @param[in] i  an input in CBOR format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if unsupported features from CBOR were\n    used in the given input @a v or if the input is not valid CBOR\n    @throw parse_error.113 if a string was expected as map key, but not found\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in CBOR\n    format to a JSON value.,from_cbor}\n\n    @sa http://cbor.io\n    @sa @ref to_cbor(const basic_json&) for the analogous serialization\n    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for the\n        related MessagePack format\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the\n        related UBJSON format\n\n    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to\n           consume input adapters, removed start_index parameter, and added\n           @a strict parameter since 3.0.0; added @a allow_exceptions parameter\n           since 3.2.0\n    */\n    static basic_json from_cbor(detail::input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::cbor, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_cbor(detail::input_adapter&&, const bool, const bool)\n    */\n    template<typename A1, typename A2,\n             detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>\n    static basic_json from_cbor(A1 && a1, A2 && a2,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::cbor, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @brief create a JSON value from an input in MessagePack format\n\n    Deserializes a given input @a i to a JSON value using the MessagePack\n    serialization format.\n\n    The library maps MessagePack types to JSON value types as follows:\n\n    MessagePack type | JSON value type | first byte\n    ---------------- | --------------- | ----------\n    positive fixint  | number_unsigned | 0x00..0x7F\n    fixmap           | object          | 0x80..0x8F\n    fixarray         | array           | 0x90..0x9F\n    fixstr           | string          | 0xA0..0xBF\n    nil              | `null`          | 0xC0\n    false            | `false`         | 0xC2\n    true             | `true`          | 0xC3\n    float 32         | number_float    | 0xCA\n    float 64         | number_float    | 0xCB\n    uint 8           | number_unsigned | 0xCC\n    uint 16          | number_unsigned | 0xCD\n    uint 32          | number_unsigned | 0xCE\n    uint 64          | number_unsigned | 0xCF\n    int 8            | number_integer  | 0xD0\n    int 16           | number_integer  | 0xD1\n    int 32           | number_integer  | 0xD2\n    int 64           | number_integer  | 0xD3\n    str 8            | string          | 0xD9\n    str 16           | string          | 0xDA\n    str 32           | string          | 0xDB\n    array 16         | array           | 0xDC\n    array 32         | array           | 0xDD\n    map 16           | object          | 0xDE\n    map 32           | object          | 0xDF\n    negative fixint  | number_integer  | 0xE0-0xFF\n\n    @warning The mapping is **incomplete** in the sense that not all\n             MessagePack types can be converted to a JSON value. The following\n             MessagePack types are not supported and will yield parse errors:\n              - bin 8 - bin 32 (0xC4..0xC6)\n              - ext 8 - ext 32 (0xC7..0xC9)\n              - fixext 1 - fixext 16 (0xD4..0xD8)\n\n    @note Any MessagePack output created @ref to_msgpack can be successfully\n          parsed by @ref from_msgpack.\n\n    @param[in] i  an input in MessagePack format convertible to an input\n                  adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if unsupported features from MessagePack were\n    used in the given input @a i or if the input is not valid MessagePack\n    @throw parse_error.113 if a string was expected as map key, but not found\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    MessagePack format to a JSON value.,from_msgpack}\n\n    @sa http://msgpack.org\n    @sa @ref to_msgpack(const basic_json&) for the analogous serialization\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the\n        related CBOR format\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for\n        the related UBJSON format\n    @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for\n        the related BSON format\n\n    @since version 2.0.9; parameter @a start_index since 2.1.1; changed to\n           consume input adapters, removed start_index parameter, and added\n           @a strict parameter since 3.0.0; added @a allow_exceptions parameter\n           since 3.2.0\n    */\n    static basic_json from_msgpack(detail::input_adapter&& i,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_msgpack(detail::input_adapter&&, const bool, const bool)\n    */\n    template<typename A1, typename A2,\n             detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>\n    static basic_json from_msgpack(A1 && a1, A2 && a2,\n                                   const bool strict = true,\n                                   const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::msgpack, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @brief create a JSON value from an input in UBJSON format\n\n    Deserializes a given input @a i to a JSON value using the UBJSON (Universal\n    Binary JSON) serialization format.\n\n    The library maps UBJSON types to JSON value types as follows:\n\n    UBJSON type | JSON value type                         | marker\n    ----------- | --------------------------------------- | ------\n    no-op       | *no value, next value is read*          | `N`\n    null        | `null`                                  | `Z`\n    false       | `false`                                 | `F`\n    true        | `true`                                  | `T`\n    float32     | number_float                            | `d`\n    float64     | number_float                            | `D`\n    uint8       | number_unsigned                         | `U`\n    int8        | number_integer                          | `i`\n    int16       | number_integer                          | `I`\n    int32       | number_integer                          | `l`\n    int64       | number_integer                          | `L`\n    string      | string                                  | `S`\n    char        | string                                  | `C`\n    array       | array (optimized values are supported)  | `[`\n    object      | object (optimized values are supported) | `{`\n\n    @note The mapping is **complete** in the sense that any UBJSON value can\n          be converted to a JSON value.\n\n    @param[in] i  an input in UBJSON format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value\n\n    @throw parse_error.110 if the given input ends prematurely or the end of\n    file was not reached when @a strict was set to true\n    @throw parse_error.112 if a parse error occurs\n    @throw parse_error.113 if a string could not be parsed successfully\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    UBJSON format to a JSON value.,from_ubjson}\n\n    @sa http://ubjson.org\n    @sa @ref to_ubjson(const basic_json&, const bool, const bool) for the\n             analogous serialization\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the\n        related CBOR format\n    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for\n        the related MessagePack format\n    @sa @ref from_bson(detail::input_adapter&&, const bool, const bool) for\n        the related BSON format\n\n    @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0\n    */\n    static basic_json from_ubjson(detail::input_adapter&& i,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_ubjson(detail::input_adapter&&, const bool, const bool)\n    */\n    template<typename A1, typename A2,\n             detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>\n    static basic_json from_ubjson(A1 && a1, A2 && a2,\n                                  const bool strict = true,\n                                  const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::ubjson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @brief Create a JSON value from an input in BSON format\n\n    Deserializes a given input @a i to a JSON value using the BSON (Binary JSON)\n    serialization format.\n\n    The library maps BSON record types to JSON value types as follows:\n\n    BSON type       | BSON marker byte | JSON value type\n    --------------- | ---------------- | ---------------------------\n    double          | 0x01             | number_float\n    string          | 0x02             | string\n    document        | 0x03             | object\n    array           | 0x04             | array\n    binary          | 0x05             | still unsupported\n    undefined       | 0x06             | still unsupported\n    ObjectId        | 0x07             | still unsupported\n    boolean         | 0x08             | boolean\n    UTC Date-Time   | 0x09             | still unsupported\n    null            | 0x0A             | null\n    Regular Expr.   | 0x0B             | still unsupported\n    DB Pointer      | 0x0C             | still unsupported\n    JavaScript Code | 0x0D             | still unsupported\n    Symbol          | 0x0E             | still unsupported\n    JavaScript Code | 0x0F             | still unsupported\n    int32           | 0x10             | number_integer\n    Timestamp       | 0x11             | still unsupported\n    128-bit decimal float | 0x13       | still unsupported\n    Max Key         | 0x7F             | still unsupported\n    Min Key         | 0xFF             | still unsupported\n\n    @warning The mapping is **incomplete**. The unsupported mappings\n             are indicated in the table above.\n\n    @param[in] i  an input in BSON format convertible to an input adapter\n    @param[in] strict  whether to expect the input to be consumed until EOF\n                       (true by default)\n    @param[in] allow_exceptions  whether to throw exceptions in case of a\n    parse error (optional, true by default)\n\n    @return deserialized JSON value\n\n    @throw parse_error.114 if an unsupported BSON record type is encountered\n\n    @complexity Linear in the size of the input @a i.\n\n    @liveexample{The example shows the deserialization of a byte vector in\n    BSON format to a JSON value.,from_bson}\n\n    @sa http://bsonspec.org/spec.html\n    @sa @ref to_bson(const basic_json&) for the analogous serialization\n    @sa @ref from_cbor(detail::input_adapter&&, const bool, const bool) for the\n        related CBOR format\n    @sa @ref from_msgpack(detail::input_adapter&&, const bool, const bool) for\n        the related MessagePack format\n    @sa @ref from_ubjson(detail::input_adapter&&, const bool, const bool) for the\n        related UBJSON format\n    */\n    static basic_json from_bson(detail::input_adapter&& i,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(i)).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n    /*!\n    @copydoc from_bson(detail::input_adapter&&, const bool, const bool)\n    */\n    template<typename A1, typename A2,\n             detail::enable_if_t<std::is_constructible<detail::input_adapter, A1, A2>::value, int> = 0>\n    static basic_json from_bson(A1 && a1, A2 && a2,\n                                const bool strict = true,\n                                const bool allow_exceptions = true)\n    {\n        basic_json result;\n        detail::json_sax_dom_parser<basic_json> sdp(result, allow_exceptions);\n        const bool res = binary_reader(detail::input_adapter(std::forward<A1>(a1), std::forward<A2>(a2))).sax_parse(input_format_t::bson, &sdp, strict);\n        return res ? result : basic_json(value_t::discarded);\n    }\n\n\n\n    /// @}\n\n    //////////////////////////\n    // JSON Pointer support //\n    //////////////////////////\n\n    /// @name JSON Pointer functions\n    /// @{\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Uses a JSON pointer to retrieve a reference to the respective JSON value.\n    No bound checking is performed. Similar to @ref operator[](const typename\n    object_t::key_type&), `null` values are created in arrays and objects if\n    necessary.\n\n    In particular:\n    - If the JSON pointer points to an object key that does not exist, it\n      is created an filled with a `null` value before a reference to it\n      is returned.\n    - If the JSON pointer points to an array index that does not exist, it\n      is created an filled with a `null` value before a reference to it\n      is returned. All indices between the current maximum and the given\n      index are also filled with `null`.\n    - The special value `-` is treated as a synonym for the index past the\n      end.\n\n    @param[in] ptr  a JSON pointer\n\n    @return reference to the element pointed to by @a ptr\n\n    @complexity Constant.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n\n    @liveexample{The behavior is shown in the example.,operatorjson_pointer}\n\n    @since version 2.0.0\n    */\n    reference operator[](const json_pointer& ptr)\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Uses a JSON pointer to retrieve a reference to the respective JSON value.\n    No bound checking is performed. The function does not change the JSON\n    value; no `null` values are created. In particular, the the special value\n    `-` yields an exception.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return const reference to the element pointed to by @a ptr\n\n    @complexity Constant.\n\n    @throw parse_error.106   if an array index begins with '0'\n    @throw parse_error.109   if an array index was not a number\n    @throw out_of_range.402  if the array index '-' is used\n    @throw out_of_range.404  if the JSON pointer can not be resolved\n\n    @liveexample{The behavior is shown in the example.,operatorjson_pointer_const}\n\n    @since version 2.0.0\n    */\n    const_reference operator[](const json_pointer& ptr) const\n    {\n        return ptr.get_unchecked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Returns a reference to the element at with specified JSON pointer @a ptr,\n    with bounds checking.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return reference to the element pointed to by @a ptr\n\n    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr\n    begins with '0'. See example below.\n\n    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr\n    is not a number. See example below.\n\n    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr\n    is out of range. See example below.\n\n    @throw out_of_range.402 if the array index '-' is used in the passed JSON\n    pointer @a ptr. As `at` provides checked access (and no elements are\n    implicitly inserted), the index '-' is always invalid. See example below.\n\n    @throw out_of_range.403 if the JSON pointer describes a key of an object\n    which cannot be found. See example below.\n\n    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.\n    See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 2.0.0\n\n    @liveexample{The behavior is shown in the example.,at_json_pointer}\n    */\n    reference at(const json_pointer& ptr)\n    {\n        return ptr.get_checked(this);\n    }\n\n    /*!\n    @brief access specified element via JSON Pointer\n\n    Returns a const reference to the element at with specified JSON pointer @a\n    ptr, with bounds checking.\n\n    @param[in] ptr  JSON pointer to the desired element\n\n    @return reference to the element pointed to by @a ptr\n\n    @throw parse_error.106 if an array index in the passed JSON pointer @a ptr\n    begins with '0'. See example below.\n\n    @throw parse_error.109 if an array index in the passed JSON pointer @a ptr\n    is not a number. See example below.\n\n    @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr\n    is out of range. See example below.\n\n    @throw out_of_range.402 if the array index '-' is used in the passed JSON\n    pointer @a ptr. As `at` provides checked access (and no elements are\n    implicitly inserted), the index '-' is always invalid. See example below.\n\n    @throw out_of_range.403 if the JSON pointer describes a key of an object\n    which cannot be found. See example below.\n\n    @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved.\n    See example below.\n\n    @exceptionsafety Strong guarantee: if an exception is thrown, there are no\n    changes in the JSON value.\n\n    @complexity Constant.\n\n    @since version 2.0.0\n\n    @liveexample{The behavior is shown in the example.,at_json_pointer_const}\n    */\n    const_reference at(const json_pointer& ptr) const\n    {\n        return ptr.get_checked(this);\n    }\n\n    /*!\n    @brief return flattened JSON value\n\n    The function creates a JSON object whose keys are JSON pointers (see [RFC\n    6901](https://tools.ietf.org/html/rfc6901)) and whose values are all\n    primitive. The original JSON value can be restored using the @ref\n    unflatten() function.\n\n    @return an object that maps JSON pointers to primitive values\n\n    @note Empty objects and arrays are flattened to `null` and will not be\n          reconstructed correctly by the @ref unflatten() function.\n\n    @complexity Linear in the size the JSON value.\n\n    @liveexample{The following code shows how a JSON object is flattened to an\n    object whose keys consist of JSON pointers.,flatten}\n\n    @sa @ref unflatten() for the reverse function\n\n    @since version 2.0.0\n    */\n    basic_json flatten() const\n    {\n        basic_json result(value_t::object);\n        json_pointer::flatten(\"\", *this, result);\n        return result;\n    }\n\n    /*!\n    @brief unflatten a previously flattened JSON value\n\n    The function restores the arbitrary nesting of a JSON value that has been\n    flattened before using the @ref flatten() function. The JSON value must\n    meet certain constraints:\n    1. The value must be an object.\n    2. The keys must be JSON pointers (see\n       [RFC 6901](https://tools.ietf.org/html/rfc6901))\n    3. The mapped values must be primitive JSON types.\n\n    @return the original JSON from a flattened version\n\n    @note Empty objects and arrays are flattened by @ref flatten() to `null`\n          values and can not unflattened to their original type. Apart from\n          this example, for a JSON value `j`, the following is always true:\n          `j == j.flatten().unflatten()`.\n\n    @complexity Linear in the size the JSON value.\n\n    @throw type_error.314  if value is not an object\n    @throw type_error.315  if object values are not primitive\n\n    @liveexample{The following code shows how a flattened JSON object is\n    unflattened into the original nested JSON object.,unflatten}\n\n    @sa @ref flatten() for the reverse function\n\n    @since version 2.0.0\n    */\n    basic_json unflatten() const\n    {\n        return json_pointer::unflatten(*this);\n    }\n\n    /// @}\n\n    //////////////////////////\n    // JSON Patch functions //\n    //////////////////////////\n\n    /// @name JSON Patch functions\n    /// @{\n\n    /*!\n    @brief applies a JSON patch\n\n    [JSON Patch](http://jsonpatch.com) defines a JSON document structure for\n    expressing a sequence of operations to apply to a JSON) document. With\n    this function, a JSON Patch is applied to the current JSON value by\n    executing all operations from the patch.\n\n    @param[in] json_patch  JSON patch document\n    @return patched document\n\n    @note The application of a patch is atomic: Either all operations succeed\n          and the patched document is returned or an exception is thrown. In\n          any case, the original value is not changed: the patch is applied\n          to a copy of the value.\n\n    @throw parse_error.104 if the JSON patch does not consist of an array of\n    objects\n\n    @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory\n    attributes are missing); example: `\"operation add must have member path\"`\n\n    @throw out_of_range.401 if an array index is out of range.\n\n    @throw out_of_range.403 if a JSON pointer inside the patch could not be\n    resolved successfully in the current JSON value; example: `\"key baz not\n    found\"`\n\n    @throw out_of_range.405 if JSON pointer has no parent (\"add\", \"remove\",\n    \"move\")\n\n    @throw other_error.501 if \"test\" operation was unsuccessful\n\n    @complexity Linear in the size of the JSON value and the length of the\n    JSON patch. As usually only a fraction of the JSON value is affected by\n    the patch, the complexity can usually be neglected.\n\n    @liveexample{The following code shows how a JSON patch is applied to a\n    value.,patch}\n\n    @sa @ref diff -- create a JSON patch by comparing two JSON values\n\n    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)\n    @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901)\n\n    @since version 2.0.0\n    */\n    basic_json patch(const basic_json& json_patch) const\n    {\n        // make a working copy to apply the patch to\n        basic_json result = *this;\n\n        // the valid JSON Patch operations\n        enum class patch_operations {add, remove, replace, move, copy, test, invalid};\n\n        const auto get_op = [](const std::string & op)\n        {\n            if (op == \"add\")\n            {\n                return patch_operations::add;\n            }\n            if (op == \"remove\")\n            {\n                return patch_operations::remove;\n            }\n            if (op == \"replace\")\n            {\n                return patch_operations::replace;\n            }\n            if (op == \"move\")\n            {\n                return patch_operations::move;\n            }\n            if (op == \"copy\")\n            {\n                return patch_operations::copy;\n            }\n            if (op == \"test\")\n            {\n                return patch_operations::test;\n            }\n\n            return patch_operations::invalid;\n        };\n\n        // wrapper for \"add\" operation; add value at ptr\n        const auto operation_add = [&result](json_pointer & ptr, basic_json val)\n        {\n            // adding to the root of the target document means replacing it\n            if (ptr.is_root())\n            {\n                result = val;\n            }\n            else\n            {\n                // make sure the top element of the pointer exists\n                json_pointer top_pointer = ptr.top();\n                if (top_pointer != ptr)\n                {\n                    result.at(top_pointer);\n                }\n\n                // get reference to parent of JSON pointer ptr\n                const auto last_path = ptr.pop_back();\n                basic_json& parent = result[ptr];\n\n                switch (parent.m_type)\n                {\n                    case value_t::null:\n                    case value_t::object:\n                    {\n                        // use operator[] to add value\n                        parent[last_path] = val;\n                        break;\n                    }\n\n                    case value_t::array:\n                    {\n                        if (last_path == \"-\")\n                        {\n                            // special case: append to back\n                            parent.push_back(val);\n                        }\n                        else\n                        {\n                            const auto idx = json_pointer::array_index(last_path);\n                            if (JSON_UNLIKELY(static_cast<size_type>(idx) > parent.size()))\n                            {\n                                // avoid undefined behavior\n                                JSON_THROW(out_of_range::create(401, \"array index \" + std::to_string(idx) + \" is out of range\"));\n                            }\n\n                            // default case: insert add offset\n                            parent.insert(parent.begin() + static_cast<difference_type>(idx), val);\n                        }\n                        break;\n                    }\n\n                    // LCOV_EXCL_START\n                    default:\n                    {\n                        // if there exists a parent it cannot be primitive\n                        assert(false);\n                    }\n                        // LCOV_EXCL_STOP\n                }\n            }\n        };\n\n        // wrapper for \"remove\" operation; remove value at ptr\n        const auto operation_remove = [&result](json_pointer & ptr)\n        {\n            // get reference to parent of JSON pointer ptr\n            const auto last_path = ptr.pop_back();\n            basic_json& parent = result.at(ptr);\n\n            // remove child\n            if (parent.is_object())\n            {\n                // perform range check\n                auto it = parent.find(last_path);\n                if (JSON_LIKELY(it != parent.end()))\n                {\n                    parent.erase(it);\n                }\n                else\n                {\n                    JSON_THROW(out_of_range::create(403, \"key '\" + last_path + \"' not found\"));\n                }\n            }\n            else if (parent.is_array())\n            {\n                // note erase performs range check\n                parent.erase(static_cast<size_type>(json_pointer::array_index(last_path)));\n            }\n        };\n\n        // type check: top level value must be an array\n        if (JSON_UNLIKELY(not json_patch.is_array()))\n        {\n            JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\"));\n        }\n\n        // iterate and apply the operations\n        for (const auto& val : json_patch)\n        {\n            // wrapper to get a value for an operation\n            const auto get_value = [&val](const std::string & op,\n                                          const std::string & member,\n                                          bool string_type) -> basic_json &\n            {\n                // find value\n                auto it = val.m_value.object->find(member);\n\n                // context-sensitive error message\n                const auto error_msg = (op == \"op\") ? \"operation\" : \"operation '\" + op + \"'\";\n\n                // check if desired value is present\n                if (JSON_UNLIKELY(it == val.m_value.object->end()))\n                {\n                    JSON_THROW(parse_error::create(105, 0, error_msg + \" must have member '\" + member + \"'\"));\n                }\n\n                // check if result is of type string\n                if (JSON_UNLIKELY(string_type and not it->second.is_string()))\n                {\n                    JSON_THROW(parse_error::create(105, 0, error_msg + \" must have string member '\" + member + \"'\"));\n                }\n\n                // no error: return value\n                return it->second;\n            };\n\n            // type check: every element of the array must be an object\n            if (JSON_UNLIKELY(not val.is_object()))\n            {\n                JSON_THROW(parse_error::create(104, 0, \"JSON patch must be an array of objects\"));\n            }\n\n            // collect mandatory members\n            const std::string op = get_value(\"op\", \"op\", true);\n            const std::string path = get_value(op, \"path\", true);\n            json_pointer ptr(path);\n\n            switch (get_op(op))\n            {\n                case patch_operations::add:\n                {\n                    operation_add(ptr, get_value(\"add\", \"value\", false));\n                    break;\n                }\n\n                case patch_operations::remove:\n                {\n                    operation_remove(ptr);\n                    break;\n                }\n\n                case patch_operations::replace:\n                {\n                    // the \"path\" location must exist - use at()\n                    result.at(ptr) = get_value(\"replace\", \"value\", false);\n                    break;\n                }\n\n                case patch_operations::move:\n                {\n                    const std::string from_path = get_value(\"move\", \"from\", true);\n                    json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json v = result.at(from_ptr);\n\n                    // The move operation is functionally identical to a\n                    // \"remove\" operation on the \"from\" location, followed\n                    // immediately by an \"add\" operation at the target\n                    // location with the value that was just removed.\n                    operation_remove(from_ptr);\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::copy:\n                {\n                    const std::string from_path = get_value(\"copy\", \"from\", true);\n                    const json_pointer from_ptr(from_path);\n\n                    // the \"from\" location must exist - use at()\n                    basic_json v = result.at(from_ptr);\n\n                    // The copy is functionally identical to an \"add\"\n                    // operation at the target location using the value\n                    // specified in the \"from\" member.\n                    operation_add(ptr, v);\n                    break;\n                }\n\n                case patch_operations::test:\n                {\n                    bool success = false;\n                    JSON_TRY\n                    {\n                        // check if \"value\" matches the one at \"path\"\n                        // the \"path\" location must exist - use at()\n                        success = (result.at(ptr) == get_value(\"test\", \"value\", false));\n                    }\n                    JSON_INTERNAL_CATCH (out_of_range&)\n                    {\n                        // ignore out of range errors: success remains false\n                    }\n\n                    // throw an exception if test fails\n                    if (JSON_UNLIKELY(not success))\n                    {\n                        JSON_THROW(other_error::create(501, \"unsuccessful: \" + val.dump()));\n                    }\n\n                    break;\n                }\n\n                case patch_operations::invalid:\n                {\n                    // op must be \"add\", \"remove\", \"replace\", \"move\", \"copy\", or\n                    // \"test\"\n                    JSON_THROW(parse_error::create(105, 0, \"operation value '\" + op + \"' is invalid\"));\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /*!\n    @brief creates a diff as a JSON patch\n\n    Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can\n    be changed into the value @a target by calling @ref patch function.\n\n    @invariant For two JSON values @a source and @a target, the following code\n    yields always `true`:\n    @code {.cpp}\n    source.patch(diff(source, target)) == target;\n    @endcode\n\n    @note Currently, only `remove`, `add`, and `replace` operations are\n          generated.\n\n    @param[in] source  JSON value to compare from\n    @param[in] target  JSON value to compare against\n    @param[in] path    helper value to create JSON pointers\n\n    @return a JSON patch to convert the @a source to @a target\n\n    @complexity Linear in the lengths of @a source and @a target.\n\n    @liveexample{The following code shows how a JSON patch is created as a\n    diff for two JSON values.,diff}\n\n    @sa @ref patch -- apply a JSON patch\n    @sa @ref merge_patch -- apply a JSON Merge Patch\n\n    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)\n\n    @since version 2.0.0\n    */\n    static basic_json diff(const basic_json& source, const basic_json& target,\n                           const std::string& path = \"\")\n    {\n        // the patch\n        basic_json result(value_t::array);\n\n        // if the values are the same, return empty patch\n        if (source == target)\n        {\n            return result;\n        }\n\n        if (source.type() != target.type())\n        {\n            // different types: replace value\n            result.push_back(\n            {\n                {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n            });\n        }\n        else\n        {\n            switch (source.type())\n            {\n                case value_t::array:\n                {\n                    // first pass: traverse common elements\n                    std::size_t i = 0;\n                    while (i < source.size() and i < target.size())\n                    {\n                        // recursive call to compare array values at index i\n                        auto temp_diff = diff(source[i], target[i], path + \"/\" + std::to_string(i));\n                        result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                        ++i;\n                    }\n\n                    // i now reached the end of at least one array\n                    // in a second pass, traverse the remaining elements\n\n                    // remove my remaining elements\n                    const auto end_index = static_cast<difference_type>(result.size());\n                    while (i < source.size())\n                    {\n                        // add operations in reverse order to avoid invalid\n                        // indices\n                        result.insert(result.begin() + end_index, object(\n                        {\n                            {\"op\", \"remove\"},\n                            {\"path\", path + \"/\" + std::to_string(i)}\n                        }));\n                        ++i;\n                    }\n\n                    // add other remaining elements\n                    while (i < target.size())\n                    {\n                        result.push_back(\n                        {\n                            {\"op\", \"add\"},\n                            {\"path\", path + \"/\" + std::to_string(i)},\n                            {\"value\", target[i]}\n                        });\n                        ++i;\n                    }\n\n                    break;\n                }\n\n                case value_t::object:\n                {\n                    // first pass: traverse this object's elements\n                    for (auto it = source.cbegin(); it != source.cend(); ++it)\n                    {\n                        // escape the key name to be used in a JSON patch\n                        const auto key = json_pointer::escape(it.key());\n\n                        if (target.find(it.key()) != target.end())\n                        {\n                            // recursive call to compare object values at key it\n                            auto temp_diff = diff(it.value(), target[it.key()], path + \"/\" + key);\n                            result.insert(result.end(), temp_diff.begin(), temp_diff.end());\n                        }\n                        else\n                        {\n                            // found a key that is not in o -> remove it\n                            result.push_back(object(\n                            {\n                                {\"op\", \"remove\"}, {\"path\", path + \"/\" + key}\n                            }));\n                        }\n                    }\n\n                    // second pass: traverse other object's elements\n                    for (auto it = target.cbegin(); it != target.cend(); ++it)\n                    {\n                        if (source.find(it.key()) == source.end())\n                        {\n                            // found a key that is not in this -> add it\n                            const auto key = json_pointer::escape(it.key());\n                            result.push_back(\n                            {\n                                {\"op\", \"add\"}, {\"path\", path + \"/\" + key},\n                                {\"value\", it.value()}\n                            });\n                        }\n                    }\n\n                    break;\n                }\n\n                default:\n                {\n                    // both primitive type: replace value\n                    result.push_back(\n                    {\n                        {\"op\", \"replace\"}, {\"path\", path}, {\"value\", target}\n                    });\n                    break;\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /// @}\n\n    ////////////////////////////////\n    // JSON Merge Patch functions //\n    ////////////////////////////////\n\n    /// @name JSON Merge Patch functions\n    /// @{\n\n    /*!\n    @brief applies a JSON Merge Patch\n\n    The merge patch format is primarily intended for use with the HTTP PATCH\n    method as a means of describing a set of modifications to a target\n    resource's content. This function applies a merge patch to the current\n    JSON value.\n\n    The function implements the following algorithm from Section 2 of\n    [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396):\n\n    ```\n    define MergePatch(Target, Patch):\n      if Patch is an Object:\n        if Target is not an Object:\n          Target = {} // Ignore the contents and set it to an empty Object\n        for each Name/Value pair in Patch:\n          if Value is null:\n            if Name exists in Target:\n              remove the Name/Value pair from Target\n          else:\n            Target[Name] = MergePatch(Target[Name], Value)\n        return Target\n      else:\n        return Patch\n    ```\n\n    Thereby, `Target` is the current object; that is, the patch is applied to\n    the current value.\n\n    @param[in] apply_patch  the patch to apply\n\n    @complexity Linear in the lengths of @a patch.\n\n    @liveexample{The following code shows how a JSON Merge Patch is applied to\n    a JSON document.,merge_patch}\n\n    @sa @ref patch -- apply a JSON patch\n    @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396)\n\n    @since version 3.0.0\n    */\n    void merge_patch(const basic_json& apply_patch)\n    {\n        if (apply_patch.is_object())\n        {\n            if (not is_object())\n            {\n                *this = object();\n            }\n            for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it)\n            {\n                if (it.value().is_null())\n                {\n                    erase(it.key());\n                }\n                else\n                {\n                    operator[](it.key()).merge_patch(it.value());\n                }\n            }\n        }\n        else\n        {\n            *this = apply_patch;\n        }\n    }\n\n    /// @}\n};\n} // namespace nlohmann\n\n///////////////////////\n// nonmember support //\n///////////////////////\n\n// specialization of std::swap, and std::hash\nnamespace std\n{\n\n/// hash value for JSON objects\ntemplate<>\nstruct hash<nlohmann::json>\n{\n    /*!\n    @brief return a hash value for a JSON object\n\n    @since version 1.0.0\n    */\n    std::size_t operator()(const nlohmann::json& j) const\n    {\n        // a naive hashing via the string representation\n        const auto& h = hash<nlohmann::json::string_t>();\n        return h(j.dump());\n    }\n};\n\n/// specialization for std::less<value_t>\n/// @note: do not remove the space after '<',\n///        see https://github.com/nlohmann/json/pull/679\ntemplate<>\nstruct less< ::nlohmann::detail::value_t>\n{\n    /*!\n    @brief compare two value_t enum values\n    @since version 3.0.0\n    */\n    bool operator()(nlohmann::detail::value_t lhs,\n                    nlohmann::detail::value_t rhs) const noexcept\n    {\n        return nlohmann::detail::operator<(lhs, rhs);\n    }\n};\n\n/*!\n@brief exchanges the values of two JSON objects\n\n@since version 1.0.0\n*/\ntemplate<>\ninline void swap<nlohmann::json>(nlohmann::json& j1, nlohmann::json& j2) noexcept(\n    is_nothrow_move_constructible<nlohmann::json>::value and\n    is_nothrow_move_assignable<nlohmann::json>::value\n)\n{\n    j1.swap(j2);\n}\n\n} // namespace std\n\n/*!\n@brief user-defined string literal for JSON values\n\nThis operator implements a user-defined string literal for JSON objects. It\ncan be used by adding `\"_json\"` to a string literal and returns a JSON object\nif no parse error occurred.\n\n@param[in] s  a string representation of a JSON object\n@param[in] n  the length of string @a s\n@return a JSON object\n\n@since version 1.0.0\n*/\ninline nlohmann::json operator \"\" _json(const char* s, std::size_t n)\n{\n    return nlohmann::json::parse(s, s + n);\n}\n\n/*!\n@brief user-defined string literal for JSON pointer\n\nThis operator implements a user-defined string literal for JSON Pointers. It\ncan be used by adding `\"_json_pointer\"` to a string literal and returns a JSON pointer\nobject if no parse error occurred.\n\n@param[in] s  a string representation of a JSON Pointer\n@param[in] n  the length of string @a s\n@return a JSON pointer object\n\n@since version 2.0.0\n*/\ninline nlohmann::json::json_pointer operator \"\" _json_pointer(const char* s, std::size_t n)\n{\n    return nlohmann::json::json_pointer(std::string(s, n));\n}\n\n// #include <nlohmann/detail/macro_unscope.hpp>\n\n\n// restore GCC/clang diagnostic settings\n#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)\n    #pragma GCC diagnostic pop\n#endif\n#if defined(__clang__)\n    #pragma GCC diagnostic pop\n#endif\n\n// clean up\n#undef JSON_INTERNAL_CATCH\n#undef JSON_CATCH\n#undef JSON_THROW\n#undef JSON_TRY\n#undef JSON_LIKELY\n#undef JSON_UNLIKELY\n#undef JSON_DEPRECATED\n#undef JSON_HAS_CPP_14\n#undef JSON_HAS_CPP_17\n#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION\n#undef NLOHMANN_BASIC_JSON_TPL\n\n\n#endif\n"
  },
  {
    "path": "srv/WholeImageDescriptorCompute.srv",
    "content": "sensor_msgs/Image ima\nint64 a\n---\nfloat64[] desc\nstring model_type\n"
  }
]