├── .clang-format
├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── assets
├── KIT_passat.mtl
├── KIT_passat.obj
├── KIT_tire.mtl
├── KIT_tire.obj
├── KIT_velodyne.mtl
└── KIT_velodyne.obj
├── config
├── default.xml
└── vlp16_example.xml
├── external
├── find_dependencies.cmake
├── glow.cmake
└── gtsam.cmake
├── src
├── core
│ ├── Frame.h
│ ├── Frame2Model.cpp
│ ├── Frame2Model.h
│ ├── ImagePyramidGenerator.cpp
│ ├── ImagePyramidGenerator.h
│ ├── LieGaussNewton.cpp
│ ├── LieGaussNewton.h
│ ├── Objective.h
│ ├── Posegraph.cpp
│ ├── Posegraph.h
│ ├── Preprocessing.cpp
│ ├── Preprocessing.h
│ ├── Surfel.h
│ ├── SurfelMap.cpp
│ ├── SurfelMap.h
│ ├── SurfelMapping.cpp
│ ├── SurfelMapping.h
│ ├── lie_algebra.cpp
│ └── lie_algebra.h
├── genvideo.cpp
├── io
│ ├── KITTIReader.cpp
│ ├── KITTIReader.h
│ ├── LaserscanReader.h
│ ├── RobocarReader.cpp
│ ├── RobocarReader.h
│ ├── SimulationReader.cpp
│ └── SimulationReader.h
├── opengl
│ ├── MatrixStack.h
│ ├── Mesh.cpp
│ ├── Mesh.h
│ ├── Model.cpp
│ ├── Model.h
│ ├── ObjReader.cpp
│ └── ObjReader.h
├── rv
│ ├── CompositeParameter.cpp
│ ├── CompositeParameter.h
│ ├── Exception.h
│ ├── FileUtil.cpp
│ ├── FileUtil.h
│ ├── IOError.h
│ ├── Laserscan.cpp
│ ├── Laserscan.h
│ ├── LaserscanReader.h
│ ├── Math.cpp
│ ├── Math.h
│ ├── Parameter.cpp
│ ├── Parameter.h
│ ├── ParameterList.cpp
│ ├── ParameterList.h
│ ├── ParameterListIterator.cpp
│ ├── ParameterListIterator.h
│ ├── PrimitiveParameters.cpp
│ ├── PrimitiveParameters.h
│ ├── Random.cpp
│ ├── Random.h
│ ├── RangeParameter.cpp
│ ├── RangeParameter.h
│ ├── RingBuffer.h
│ ├── Stopwatch.cpp
│ ├── Stopwatch.h
│ ├── XmlDocument.cpp
│ ├── XmlDocument.h
│ ├── XmlError.h
│ ├── XmlNode.cpp
│ ├── XmlNode.h
│ ├── geometry.h
│ ├── point_traits.h
│ ├── string_utils.cpp
│ ├── string_utils.h
│ ├── transform.cpp
│ ├── transform.h
│ ├── yxml.c
│ └── yxml.h
├── shader
│ ├── Frame2Model_jacobians.frag
│ ├── Frame2Model_jacobians.geom
│ ├── Frame2Model_jacobians.vert
│ ├── avg_vertexmap.frag
│ ├── bilateral_filter.frag
│ ├── color.glsl
│ ├── coloredvertices.frag
│ ├── coloredvertices.vert
│ ├── copy_surfels.geom
│ ├── copy_surfels.vert
│ ├── draw_depthimg.frag
│ ├── draw_mesh.frag
│ ├── draw_mesh.vert
│ ├── draw_normalmap.frag
│ ├── draw_normalmap3d.geom
│ ├── draw_normalmap3d.vert
│ ├── draw_posegraph_edge.geom
│ ├── draw_residuals.frag
│ ├── draw_submaps.geom
│ ├── draw_submaps.vert
│ ├── draw_surfelPoints.vert
│ ├── draw_surfels.frag
│ ├── draw_surfels.geom
│ ├── draw_surfels.vert
│ ├── draw_surfels_coords.geom
│ ├── draw_vertexmap3d.vert
│ ├── empty.frag
│ ├── empty.vert
│ ├── extract_surfels.vert
│ ├── gen_depthimg.frag
│ ├── gen_depthimg.vert
│ ├── gen_indexmap.frag
│ ├── gen_indexmap.vert
│ ├── gen_normalmap.frag
│ ├── gen_surfels.frag
│ ├── gen_surfels.geom
│ ├── gen_surfels.vert
│ ├── gen_surfels330.geom
│ ├── gen_vertexmap.frag
│ ├── gen_vertexmap.vert
│ ├── init_depthimg.frag
│ ├── init_radiusConf.frag
│ ├── init_radiusConf.vert
│ ├── laserscan.frag
│ ├── laserscan.vert
│ ├── passthrough.frag
│ ├── quad.geom
│ ├── render_compose.frag
│ ├── render_surfels.frag
│ ├── render_surfels.geom
│ ├── render_surfels.vert
│ ├── update_surfels.frag
│ ├── update_surfels.geom
│ └── update_surfels.vert
├── util
│ ├── Log.cpp
│ ├── Log.h
│ ├── ScanAccumulator.cpp
│ ├── ScanAccumulator.h
│ ├── kitti_utils.cpp
│ └── kitti_utils.h
└── visualizer
│ ├── GraphWidget.cpp
│ ├── GraphWidget.h
│ ├── ViewportWidget.cpp
│ ├── ViewportWidget.h
│ ├── VisualizerWindow.cpp
│ ├── VisualizerWindow.h
│ ├── visualizer.cpp
│ └── visualizer.ui
└── test
├── CMakeLists.txt
├── calib.txt
├── core
├── CalibTest.cpp
├── EvalTest.cpp
├── PosegraphTest.cpp
├── PyramidTest.cpp
├── lie_test.cpp
├── matrix.cpp
└── matrix.h
├── gt_poses.txt
├── opengl
├── depthimg-test.cpp
├── icp-test.cpp
├── jacobian-test.cpp
├── main-opengl.cpp
├── ndc.frag
├── ndc.vert
├── pix2ndc.frag
├── pix2ndc.vert
├── rounding.frag
├── rounding.vert
├── rounding_access.frag
├── rounding_access_rect.frag
├── test.frag
└── testNDC.cpp
├── result_poses.txt
├── rv
├── fileutil-test.cpp
├── geometry-test.cpp
├── math-test.cpp
├── parameter-test.cpp
├── parameterlist-test.cpp
├── parameterlistiterator-test.cpp
├── transform-test.cpp
└── xml-test.cpp
├── scan0.bin
├── scan1.bin
└── test_helpers.h
/.clang-format:
--------------------------------------------------------------------------------
1 | BasedOnStyle: Google
2 | IndentWidth: 2
3 | ColumnLimit: 120
4 | AllowShortFunctionsOnASingleLine: Inline
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/c++,cmake,cuda,qt
2 |
3 | ### C++ ###
4 | # Compiled Object files
5 | *.slo
6 | *.lo
7 | *.o
8 | *.obj
9 |
10 | # Precompiled Headers
11 | *.gch
12 | *.pch
13 |
14 | # Compiled Dynamic libraries
15 | *.so
16 | *.dylib
17 | *.dll
18 |
19 | # Fortran module files
20 | *.mod
21 |
22 | # Compiled Static libraries
23 | *.lai
24 | *.la
25 | *.a
26 | *.lib
27 |
28 | # Executables
29 | *.exe
30 | *.out
31 | *.app
32 |
33 |
34 | ### CMake ###
35 | CMakeCache.txt
36 | CMakeFiles
37 | CMakeScripts
38 | Makefile
39 | cmake_install.cmake
40 | install_manifest.txt
41 |
42 |
43 | ### CUDA ###
44 | *.i
45 | *.ii
46 | *.gpu
47 | *.ptx
48 | *.cubin
49 | *.fatbin
50 |
51 |
52 | ### Qt ###
53 | # C++ objects and libs
54 |
55 | *.slo
56 | *.lo
57 | *.o
58 | *.a
59 | *.la
60 | *.lai
61 | *.so
62 | *.dll
63 | *.dylib
64 |
65 | # Qt-es
66 |
67 | /.qmake.cache
68 | /.qmake.stash
69 | *.pro.user
70 | *.pro.user.*
71 | *.qbs.user
72 | *.qbs.user.*
73 | *.moc
74 | moc_*.cpp
75 | qrc_*.cpp
76 | ui_*.h
77 | Makefile*
78 | *build-*
79 |
80 | # QtCreator
81 |
82 | *.autosave
83 |
84 | # QtCtreator Qml
85 | *.qmlproject.user
86 | *.qmlproject.user.*
87 |
88 | # QtCtreator CMake
89 | CMakeLists.txt.user
90 |
91 | # project-related files
92 | build
93 | .*
94 | IDEAS.md
95 | *~
96 | evaluation/result
97 | *.pyc
98 |
99 | # generated folders
100 | bin
101 | lib
102 |
103 | evaluation
104 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016-2018 Jens Behley
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Surfel-based Mapping using 3D Laser Range Data
2 |
3 | Mapping of 3d laser range data from a rotating laser range scanner, e.g., the Velodyne HDL-64E.
4 | For representing the map, we use surfels that enables fast rendering of the map for point-to-plane ICP and loop closure detection.
5 |
6 | ## Publication
7 |
8 | If you use our implementation in your academic work, please cite the corresponding paper:
9 |
10 | J. Behley, C. Stachniss. *Efficient Surfel-Based SLAM using 3D Laser Range Data in Urban Environments*, Proc. of Robotics: Science and Systems (RSS), 2018.
11 |
12 | The BibTeX entry for the paper is:
13 |
14 | @inproceedings{behley2018rss,
15 | author = {Jens Behley and Cyrill Stachniss},
16 | title = {Efficient Surfel-Based SLAM using 3D Laser Range Data in Urban Environments},
17 | booktitle = {Proc.~of Robotics: Science and Systems~(RSS)},
18 | year = {2018}
19 | }
20 |
21 |
22 | ## Dependencies
23 |
24 | * Qt5 >= 5.2.1
25 | * OpenGL >= 3.3
26 | * libEigen >= 3.2
27 |
28 | In Ubuntu 22.04/20.04: Installing all dependencies is accomplished by:
29 | ```bash
30 | $ sudo apt-get install -y build-essential cmake libeigen3-dev libboost-all-dev qtbase5-dev libglew-dev
31 | ```
32 |
33 | ## Build
34 |
35 | ```
36 | $ mkdir build && cd build
37 | $ cmake .. -DCMAKE_BUILD_TYPE=Release -DOPENGL_VERSION=430 -DENABLE_NVIDIA_EXT=YES
38 | $ make -j5
39 | ```
40 |
41 | Where you have to set `OPENGL_VERSION` to the supported OpenGL core profile version of your system, which you can query as follows:
42 |
43 | ```bash
44 | $ glxinfo | grep "version"
45 | server glx version string: 1.4
46 | client glx version string: 1.4
47 | GLX version: 1.4
48 | OpenGL core profile version string: 4.3.0 NVIDIA 367.44
49 | OpenGL core profile shading language version string: 4.30 NVIDIA [...]
50 | OpenGL version string: 4.5.0 NVIDIA 367.44
51 | OpenGL shading language version string: 4.50 NVIDIA
52 | ```
53 |
54 | Here the line `OpenGL core profile version string: 4.3.0 NVIDIA 367.44` is important and therefore you should use `-DOPENGL_VERSION = 430`. If you are unsure you can also leave it on the default version `330`, which should be supported by all OpenGL-capable devices.
55 |
56 | If you have a NVIDIA device, like a Geforce or Quadro graphics card, you should also activate the NVIDIA extensions using `-DENABLE_NVIDIA_EXT=YES` for info about the current GPU memory usage of the program.
57 |
58 | Now the project root directory should contain a `bin` directory containing the visualizer.
59 |
60 | ## How to run and use it?
61 |
62 | All binaries are copied to the `bin` directory of the source folder of the project. Thus,
63 | 1. run `visualizer` in the `bin` directory,
64 | 2. open a Velodyne directory from the KITTI Visual Odometry Benchmark and select a ".bin" file,
65 | 3. start the processing of the scans via the "play button" in the GUI.
66 |
67 | In the `config` directory, different configuration files are given, which can be used as reference to set parameters for some experiments with other data. Specifying the right "vertical Field-of-View" (`data_fov_up` and `data_fov_down`) and the right number of scan lines (`data_height`) are the most important parameters.
68 |
69 | See also the [project page](http://jbehley.github.io/projects/surfel_mapping/) for configuration files used for the evaluation in the paper.
70 |
71 | ## License
72 |
73 |
74 | Copyright 2018 Jens Behley, University of Bonn.
75 |
76 | This project is free software made available under the MIT License. For details see the LICENSE file.
77 |
78 |
79 |
--------------------------------------------------------------------------------
/assets/KIT_passat.mtl:
--------------------------------------------------------------------------------
1 | # inside rear stuff; non visible.
2 | newmtl material0
3 | Ka 0.01 0.01 0.01
4 | Kd 1 1 1
5 | Ks 1 1 1
6 | Ke 0 0 0
7 | Ns 128
8 | d 1
9 | # roof profiles.
10 | newmtl material1
11 | Ka 0.01 0.01 0.01
12 | Kd 0.752941 0.752941 0.752941
13 | Ks 0.376471 0.376471 0.376471
14 | Ke 0 0 0
15 | Ns 8
16 | d 1
17 | # velodyne box
18 | newmtl material2
19 | Ka 0 0 0
20 | Kd 0.607843 0.607843 0.607843
21 | Ks 0.2 0.2 0.2
22 | Ke 0 0 0
23 | Ns 8
24 | d 1
25 | # nozzle on the roof
26 | newmtl material3
27 | Ka 0 0 0
28 | Kd 0 0.0 1.0
29 | Ks 0.290196 0.290196 1
30 | Ke 0 0 0
31 | Ns 8
32 | d 1
33 | # brake light
34 | newmtl material4
35 | Ka 0 0 0
36 | Kd 0.709804 0 0
37 | Ks 1 0 0
38 | Ke 0.0 0.0 0
39 | Ns 128
40 | d 1
41 | # ???
42 | newmtl material5
43 | Ka 0 0 0
44 | Kd 0.6 0.6 0.6
45 | Ks 1 0 0
46 | Ke 0 0 0
47 | Ns 128
48 | d 0.4
49 | # side and back glass tint
50 | newmtl material6
51 | Ka 0 0 0
52 | Kd 0.003922 0.078431 0
53 | Ks 1 1 1
54 | Ke 0 0 0
55 | Ns 90.5097
56 | d 0.1
57 | # brake lights glass
58 | newmtl material7
59 | Ka 0 0 0
60 | Kd 1 0.470588 0
61 | Ks 1 0.470588 0
62 | Ke 0 0 0
63 | Ns 128
64 | d 0.4
65 | # hood at the back // changed to black
66 | newmtl material8
67 | Ka 0.05 0.05 0.05
68 | Kd 0.1 0.1 0.1
69 | # Ks 0.290196 0.290196 1
70 | Ks 0.2 0.2 0.2
71 | Ke 0 0 0
72 | #Ns 64
73 | Ns 32
74 | d 1
75 | # blackish window tint
76 | newmtl material9
77 | Ka 0.2 0.2 0.2
78 | Kd 0 0 0
79 | Ks 0.2 0.2 0.2
80 | Ke 0 0 0
81 | Ns 4
82 | d 1
83 | # inside cover material
84 | newmtl material10
85 | Ka 0 0 0
86 | Kd 0.498039 0.498039 0.498039
87 | Ks 0.5 0.5 0.5
88 | Ke 0 0 0
89 | Ns 32
90 | d 1
91 | # rear windshield wiper
92 | newmtl material11
93 | Ka 0 0 0
94 | #Kd 0.74902 0.74902 0.74902
95 | Kd 0.04902 0.04902 0.04902
96 | Ks 0.2 0.2 0.2
97 | Ke 0 0 0
98 | Ns 2
99 | d 1
100 | # cockpit
101 | newmtl material12
102 | Ka 0 0 0
103 | Kd 0.498039 0.498039 0.498039
104 | Ks 0.2 0.2 0.2
105 | Ke 0 0 0
106 | Ns 2
107 | d 1
108 | # CAR PAINT
109 | newmtl material13
110 | Ka 0 0 0
111 | Kd 0 0 1
112 | Ks 0.423529 0.423529 1
113 | Ke 0 0 0
114 | # Ns 8
115 | Ns 128
116 | d 1
117 | # front window tint
118 | newmtl material14
119 | Ka 0 0 0
120 | Kd 1 1 1
121 | Ks 1 1 1
122 | Ke 0 0 0
123 | Ns 90.5097
124 | d 0.25
125 | # inside material of seats
126 | newmtl material15
127 | Ka 0 0 0
128 | Kd 0.139516 0.139516 0.139516
129 | Ks 0.033146 0.033146 0.033146
130 | Ke 0 0 0
131 | Ns 8
132 | d 1
133 | # turn signals front
134 | newmtl material16
135 | Ka 0 0 0
136 | Kd 1 0.470588 0
137 | Ks 1 0.470588 0
138 | Ke 0.1 0.02 0
139 | Ns 128
140 | d 1
141 | # plastic on car
142 | newmtl material17
143 | Ka 0 0 0
144 | Kd 0.164491 0.164491 0.164491
145 | Kd 0.05 0.05 0.05
146 | Ks 0.2 0.2 0.2
147 | Ke 0 0 0
148 | Ns 2
149 | d 1
150 | # grill, VW logo, material, etc.
151 | newmtl material18
152 | Ka 0 0 0
153 | Kd 0.5 0.5 0.5
154 | Ks 1 1 1
155 | Ke 0 0 0
156 | Ns 128
157 | d 1
158 | # licence plate
159 | newmtl material19
160 | Ka 0.2 0.2 0.2
161 | Kd 1 1 1
162 | Ks 0.2 0.2 0.2
163 | Ke 0 0 0
164 | Ns 128
165 | d 1
166 |
--------------------------------------------------------------------------------
/assets/KIT_tire.mtl:
--------------------------------------------------------------------------------
1 | newmtl material0
2 | Ka 0 0 0
3 | Kd 0.5 0.5 0.5
4 | Ks 1 1 1
5 | Ke 0 0 0
6 | Ns 32
7 | d 1
8 | newmtl material1
9 | Ka 0 0 0
10 | Kd 1 1 1
11 | Ks 1 1 1
12 | Ke 0 0 0
13 | Ns 128
14 | d 1
15 | newmtl material2
16 | Ka 0 0 0
17 | Kd 0.164491 0.164491 0.164491
18 | Ks 0.2 0.2 0.2
19 | Ke 0 0 0
20 | Ns 2
21 | d 1
22 |
--------------------------------------------------------------------------------
/assets/KIT_velodyne.mtl:
--------------------------------------------------------------------------------
1 | newmtl material0
2 | Ka 0.2 0.2 0.2
3 | Kd 0 0 0
4 | Ks 0.2 0.2 0.2
5 | Ke 0 0 0
6 | Ns 8
7 | d 0.85
8 | newmtl material1
9 | Ka 0 0 0
10 | Kd 0.784314 0.784314 0.784314
11 | Ks 0.2 0.2 0.2
12 | Ke 0 0 0
13 | Ns 8
14 | d 1
15 | newmtl material2
16 | Ka 0 0 0
17 | Kd 0.752941 0.752941 0.752941
18 | Ks 0.2 0.2 0.2
19 | Ke 0 0 0
20 | Ns 8
21 | d 1
22 |
--------------------------------------------------------------------------------
/config/default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 900
4 | 64
5 | 3.0
6 | -25.0
7 | 75.0
8 | 2.0
9 | 0.0
10 | 0.0
11 |
12 |
13 | 10
14 | 0.0001
15 | 0.0001
16 | 2.0
17 | 30.0
18 | huber
19 | 0.5
20 | false
21 | true
22 | 10.0
23 | true
24 |
25 |
26 | 900
27 | 64
28 | 3.0
29 | -25.0
30 | 100.0
31 | 0.0
32 |
33 |
34 | true
35 | 8.0
36 |
37 |
38 | true
39 | 0.5
40 | 30.0
41 | false
42 |
43 |
44 | frame-to-model
45 |
46 | 0.05
47 | 1.0
48 | 90.0
49 | 0.2
50 | 45.0
51 | 3
52 | 3
53 | 0.0
54 |
55 | 0.6
56 | 0.5
57 |
58 | 1
59 | 1
60 |
61 | true
62 |
63 | 1
64 |
65 |
66 | 4
67 | 10.0
68 | true
69 |
70 | 200
71 | 5
72 |
73 |
74 | true
75 | 1.15
76 | 0.95
77 | 1.1
78 | 50
79 | 5
80 | 50
81 |
82 |
--------------------------------------------------------------------------------
/config/vlp16_example.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 | 360
15 | 16
16 | 15
17 | -15.0
18 | 10
19 | 0.0001
20 | 0.0001
21 | 3.0
22 | 30.0
23 | 75.0
24 | 0.5
25 |
26 | 0.0
27 | 0.0
28 |
29 | huber
30 | 0.5
31 | true
32 |
33 | false
34 | 2.5
35 | 2.5
36 | false
37 |
38 | 20.0
39 | false
40 | 0.05
41 | 0.5
42 |
43 | frame-to-model
44 | 360
45 | 16
46 | 15.0
47 | -15.0
48 | 0.0
49 | 100.0
50 | 0.2
51 | 60.0
52 |
53 | 3
54 | 3
55 | 0.0
56 | 0.6
57 | 0.4
58 | 0.5
59 |
60 | 1.0
61 | 1.0
62 |
63 | false
64 | 1.0
65 | 1.0
66 |
67 | true
68 | 2.0
69 |
70 | true
71 | 1.0
72 | 45.0
73 |
74 | 50
75 | 2
76 |
77 |
78 | 100
79 | 20
80 | 150000
81 |
82 |
83 |
--------------------------------------------------------------------------------
/external/find_dependencies.cmake:
--------------------------------------------------------------------------------
1 | # not sure if we need to check if other system versions are available.
2 |
3 | include("${CMAKE_CURRENT_LIST_DIR}/glow.cmake")
4 | include("${CMAKE_CURRENT_LIST_DIR}/gtsam.cmake")
5 |
--------------------------------------------------------------------------------
/external/glow.cmake:
--------------------------------------------------------------------------------
1 | message(STATUS "[SuMa] Fetching glow.")
2 |
3 | include(FetchContent)
4 | FetchContent_Declare(glow GIT_REPOSITORY https://github.com/jbehley/glow.git)
5 |
6 | FetchContent_MakeAvailable(glow)
7 |
8 | # TODO: Can we get rid of this explicit inclusion?
9 | include(GenCppFile)
10 | include(GlowShaderCompilation)
11 |
--------------------------------------------------------------------------------
/external/gtsam.cmake:
--------------------------------------------------------------------------------
1 | message(STATUS "[SuMa] Fetching GTSAM v4.2.0.")
2 |
3 | include(FetchContent)
4 | FetchContent_Declare(gtsam GIT_REPOSITORY https://github.com/borglab/gtsam.git GIT_TAG 4.2.0)
5 |
6 | # some flags for building only necessary stuff from GTSAM.
7 | set(GTSAM_BUILD_TESTS Off CACHE BOOL "")
8 | set(GTSAM_BUILD_EXAMPLES_ALWAYS Off CACHE BOOL "")
9 | set(GTSAM_BUILD_EXAMPLES Off CACHE BOOL "")
10 | set(GTSAM_BUILD_UNSTABLE Off CACHE BOOL "")
11 | set(GTSAM_UNSTABLE_BUILD_PYTHON Off CACHE BOOL "")
12 | set(GTSAM_UNSTABLE_INSTALL_MATLAB_TOOLBOX Off CACHE BOOL "")
13 | set(GTSAM_BUILD_PYTHON Off CACHE BOOL "")
14 | set(GTSAM_INSTALL_MATLAB_TOOLBOX Off CACHE BOOL "")
15 |
16 | set(GTSAM_USE_SYSTEM_EIGEN On CACHE BOOL "") # only relevant option!
17 |
18 | FetchContent_MakeAvailable(gtsam)
--------------------------------------------------------------------------------
/src/core/Frame.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_CORE_FRAME_H_
2 | #define INCLUDE_CORE_FRAME_H_
3 |
4 | #include
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | class SurfelMap;
14 |
15 | /** \brief data class holding all information for a specific timestep.
16 | *
17 | * \author behley
18 | */
19 |
20 | class Frame {
21 | public:
22 | typedef std::shared_ptr Ptr;
23 |
24 | Frame(uint32_t w, uint32_t h)
25 | : valid(false),
26 | width(w),
27 | height(h),
28 | vertex_map(width, height, glow::TextureFormat::RGBA_FLOAT),
29 | normal_map(width, height, glow::TextureFormat::RGBA_FLOAT),
30 | residual_map(width, height, glow::TextureFormat::RGBA_FLOAT) {
31 | points.reserve(150000);
32 |
33 | // interpolation parameters.
34 | vertex_map.setMinifyingOperation(glow::TexRectMinOp::NEAREST);
35 | vertex_map.setMagnifyingOperation(glow::TexRectMagOp::NEAREST);
36 | vertex_map.setWrapOperation(glow::TexRectWrapOp::CLAMP_TO_BORDER, glow::TexRectWrapOp::CLAMP_TO_BORDER);
37 | normal_map.setMinifyingOperation(glow::TexRectMinOp::NEAREST);
38 | normal_map.setMagnifyingOperation(glow::TexRectMagOp::NEAREST);
39 | normal_map.setWrapOperation(glow::TexRectWrapOp::CLAMP_TO_BORDER, glow::TexRectWrapOp::CLAMP_TO_BORDER);
40 |
41 | CheckGlError();
42 | }
43 |
44 | void copy(const Frame& other) {
45 | assert(width == other.width && height == other.height && "Frame dimensions must match.");
46 |
47 | valid = other.valid;
48 | vertex_map.copy(other.vertex_map);
49 | normal_map.copy(other.normal_map);
50 | points.assign(other.points);
51 | residual_map.copy(other.residual_map);
52 | map = other.map;
53 |
54 | pose = other.pose;
55 | }
56 |
57 | bool valid;
58 | uint32_t width, height;
59 |
60 | glow::GlTextureRectangle vertex_map; // (x,y,z) & w encodes validity
61 | glow::GlTextureRectangle normal_map; // (x,y,z) & w encodes validity
62 |
63 | glow::GlBuffer points{glow::BufferTarget::ARRAY_BUFFER, glow::BufferUsage::DYNAMIC_DRAW};
64 |
65 | Eigen::Matrix4f pose{Eigen::Matrix4f::Identity()}; // estimated pose of that frame.
66 |
67 | std::shared_ptr map;
68 |
69 | glow::GlTextureRectangle residual_map;
70 | };
71 |
72 | #endif /* INCLUDE_CORE_FRAME_H_ */
73 |
--------------------------------------------------------------------------------
/src/core/Frame2Model.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_CORE_FRAME2MODEL2_H_
2 | #define INCLUDE_CORE_FRAME2MODEL2_H_
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include "Frame.h"
13 | #include "core/Objective.h"
14 |
15 | /** \brief Project point cloud, use projections to associate with model vertex, normal map.
16 | *
17 | * This is essentially ProjectedCloud to frame, but with computation of weights for
18 | * Iterative Re-weighted (Non-linear) Least Squares enabling to use different M-Estimators, like
19 | * Huber weights and Turkey Bi-square weights.
20 | *
21 | * This variant transforms the point cloud and tries to match this to the model frame rendered
22 | * at the last position.
23 | *
24 | * \author behley
25 | */
26 | class Frame2Model : public Objective {
27 | public:
28 | /** \brief setup objective. **/
29 | Frame2Model(const rv::ParameterList& params);
30 |
31 | /** \brief set single parameter to specific value. **/
32 | void setParameter(const rv::Parameter& param) override;
33 |
34 | void setData(const std::shared_ptr& current, const std::shared_ptr& last);
35 |
36 | /** \brief specify at which level the redisual/jacobian is computed. **/
37 | void setLevel(uint32_t lvl) override;
38 |
39 | /** \brief get maximum level (coarse-to-fine). **/
40 | uint32_t getMaxLevel() const override;
41 |
42 | uint32_t num_parameters() const;
43 |
44 | /** \brief compute residual for given increment. **/
45 | double residual(const Eigen::VectorXd& delta);
46 |
47 | /** \brief compute weighted JtJ and Jtf exploiting intermediate computations, return weighted objective F(x). */
48 | double jacobianProducts(Eigen::MatrixXd& JtJ, Eigen::MatrixXd& Jtf);
49 |
50 | protected:
51 | /** \brief updatable parameters, i.e., params that can be changed at runtime. **/
52 | void updateParameters();
53 |
54 | rv::ParameterList params_;
55 |
56 | std::shared_ptr current_;
57 | std::shared_ptr last_; // model frame.
58 |
59 | uint32_t num_residuals_{0};
60 |
61 | glow::GlSampler sampler_;
62 |
63 | glow::GlVertexArray vao_img_coords_;
64 | glow::GlBuffer vbo_img_coords_{glow::BufferTarget::ARRAY_BUFFER, glow::BufferUsage::STATIC_DRAW};
65 |
66 | glow::GlTexture JtJJtf_blend_;
67 | glow::GlFramebuffer fbo_blend_;
68 | glow::GlProgram program_blend_;
69 |
70 | uint32_t entriesPerKernel_{64};
71 | };
72 |
73 | #endif /* INCLUDE_CORE_FRAME2MODEL_H_ */
74 |
--------------------------------------------------------------------------------
/src/core/ImagePyramidGenerator.cpp:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | void ImagePyramidGenerator::makePyramid(uint32_t max_levels, uint32_t width, uint32_t height,
4 | std::vector& img_coords, std::vector& sizes) {
5 | sizes.resize(max_levels);
6 | std::fill(sizes.begin(), sizes.end(), glow::vec2(-1, -1));
7 |
8 | sizes[0] = glow::vec2(width, height);
9 |
10 | uint32_t new_max_level = shuffle(1, img_coords, sizes);
11 |
12 | // reorder and shrink.
13 | std::reverse(&sizes[0], &sizes[new_max_level]);
14 | sizes.resize(new_max_level);
15 | }
16 |
17 | int32_t ImagePyramidGenerator::shuffle(uint32_t level, std::vector& img_coords,
18 | std::vector& sizes) {
19 | if (level >= sizes.size()) return level; // maximum level reached.
20 |
21 | uint32_t w_old = sizes[level - 1].x, h_old = sizes[level - 1].y;
22 | if (w_old == 1 || h_old == 1) return level; // not sensible to devide further.
23 |
24 | std::vector temp(w_old * h_old);
25 |
26 | uint32_t w_new = w_old / 2, h_new = h_old;
27 |
28 | uint32_t stride = 2 << (level - 1);
29 |
30 | uint32_t next_level = 0;
31 | uint32_t prev_level = w_old * h_old - 1;
32 | for (uint32_t i = 0; i < w_old * h_old; ++i) {
33 | uint32_t x = img_coords[i].x;
34 | // uint32_t y = img_coords[i].y;
35 | if (x % stride == 0) {
36 | temp[next_level++] = img_coords[i];
37 | } else {
38 | temp[prev_level--] = img_coords[i];
39 | }
40 | }
41 |
42 | // assert(next_level == w_new * h_new);
43 | // assert(prev_level == w_old * h_old);
44 |
45 | sizes[level] = glow::vec2(w_new, h_new);
46 | // memcpy(img_coords.data(), temp.data(), temp.size() * sizeof(vec2));
47 | for (uint32_t i = 0; i < temp.size(); ++i) {
48 | img_coords[i] = temp[i];
49 | }
50 |
51 | return shuffle(level + 1, img_coords, sizes);
52 | }
53 |
--------------------------------------------------------------------------------
/src/core/ImagePyramidGenerator.h:
--------------------------------------------------------------------------------
1 | #ifndef SRC_CORE_IMAGEPYRAMIDGENERATOR_H_
2 | #define SRC_CORE_IMAGEPYRAMIDGENERATOR_H_
3 |
4 | #include
5 | #include
6 |
7 | /** \brief generate a image pyramid by reordering image coordinates.
8 | *
9 | * To get a simple way of addressing the different layers of a image pyramid,
10 | * we want to reorder the given image coordinates, such that the coordinates are
11 | * ordered according to the level from coarse-to-fine. This means the array of
12 | * image coordinates contains in the beginning all points shared by all levels.
13 | * Directly following are the coordinates of the remaining letters.
14 | *
15 | * Output is the reordered array of image coordinates and a sizes array. You can
16 | * now access the resulting array like img_coords[0:sizes[level]] and get
17 | * the corresponding image coordinate at the specific level from coarse to fine.
18 | *
19 | * This particular order should make it easy to evaluate the residual at different
20 | * levels using a vertex shader. Another advantage is that we only need a single
21 | * array of image coordinates.
22 | *
23 | * In contrast to the usual downsampling, we use always the exact same image
24 | * coordinates and do not apply something like a Gaussian blur.
25 | *
26 | * \author behley
27 | */
28 | class ImagePyramidGenerator {
29 | public:
30 | /** \brief Generate pyramid from given img_coords, returning reordered img_coords and sizes of each level.
31 | *
32 | * if it is not possible to generate max_levels of subsampled images, we simply resize sizes to max level.
33 | *
34 | * \param max_levels specifies how many levels we want to have in the end.
35 | * \param width, height of the initial image.
36 | * \param imag_coords array of image coords, which will be reordered.
37 | * \param size of resulting images at given level ordered from coarse to fine.
38 | *
39 | **/
40 | static void makePyramid(uint32_t max_levels, uint32_t width, uint32_t height, std::vector& img_coords,
41 | std::vector& sizes);
42 |
43 | protected:
44 | /** \brief recursively reorder img_coords to generate pyramid of image coordinates.
45 | *
46 | * never called directly.
47 | *
48 | * \param level currently generated level.
49 | * \param img_coords reordered image coordinates.
50 | * \param sizes array of (width, height) of each level of the pyramid.
51 | */
52 | static int32_t shuffle(uint32_t level, std::vector& img_coords, std::vector& sizes);
53 | };
54 |
55 | #endif /* SRC_CORE_IMAGEPYRAMIDGENERATOR_H_ */
56 |
--------------------------------------------------------------------------------
/src/core/LieGaussNewton.cpp:
--------------------------------------------------------------------------------
1 | #include "core/LieGaussNewton.h"
2 | #include
3 | #include
4 |
5 | using namespace rv;
6 |
7 | LieGaussNewton::LieGaussNewton() {
8 | params_.insert(IntegerParameter("max iterations", maxIter));
9 | params_.insert(FloatParameter("stopping threshold", epsilon));
10 | params_.insert(FloatParameter("delta", delta));
11 | }
12 |
13 | int32_t LieGaussNewton::minimize(Objective& F, const Eigen::Matrix4d& T0) {
14 | history_.clear();
15 |
16 | initialize(F, T0);
17 |
18 | std::vector maxIterations({maxIter, maxIter, maxIter, 3, 3, 3});
19 |
20 | for (uint32_t i = 0; i <= F.getMaxLevel(); ++i) {
21 | F.setLevel(i);
22 | k_ = 0;
23 | // std::cout << " ==== LEVEL " << i << " ==== " << std::endl;
24 | for (;;) {
25 | history_.push_back(Tk_);
26 | /** check if we can stop here. **/
27 |
28 | if (maxIterations[i] > 0 && k_ >= maxIterations[i]) break; // max iterations reached. :/
29 |
30 | int32_t result = step();
31 | // std::cout << "error = " << last_error << std::endl;
32 | if (result == 0) break; // converged.
33 |
34 | ++k_;
35 | }
36 | }
37 |
38 | return 0; // converged, hurray!
39 | }
40 |
41 | void LieGaussNewton::initialize(Objective& F, const Eigen::Matrix4d& T0) {
42 | const uint32_t N = F.num_parameters();
43 |
44 | JtJ = Eigen::MatrixXd(N, N);
45 | Jtf = Eigen::MatrixXd(N, 1);
46 |
47 | Tk_ = T0;
48 | objective_ = &F;
49 | objective_->initialize(T0);
50 | last_error = std::numeric_limits::max(); // objective_->jacobianProducts(JtJ, Jtf); // get the jacobians.
51 |
52 | if (callback_ != 0) (*callback_)(last_error, Eigen::VectorXd::Zero(N), Tk_); // t = 0
53 | }
54 |
55 | int32_t LieGaussNewton::step() {
56 | assert(objective_ != nullptr);
57 |
58 | int32_t result = 1;
59 |
60 | double current_error = objective_->jacobianProducts(JtJ, Jtf);
61 |
62 | Eigen::VectorXd deltax = JtJ.ldlt().solve(-Jtf); // new direction.
63 |
64 | assert(!std::isnan(deltax[0]));
65 |
66 | if (deltax.lpNorm() < delta) result = 0; // minimal change.
67 | if (std::abs(Jtf.maxCoeff()) < epsilon) result = 0; // gradient.
68 | if (current_error < last_error && std::abs(current_error - last_error) < epsilon) result = 0; // converged.
69 |
70 | objective_->increment(deltax);
71 |
72 | Tk_ = objective_->pose();
73 | last_error = current_error;
74 | covariance_ = JtJ;
75 | information_ = JtJ;
76 | covDirty_ = true;
77 |
78 | if (callback_ != nullptr) (*callback_)(current_error, deltax, Tk_);
79 |
80 | return result;
81 | }
82 |
83 | void LieGaussNewton::setParameters(const ParameterList& params) {
84 | for (ParameterList::const_iterator it = params.begin(); it != params.end(); ++it) {
85 | if (params_.hasParam(it->name())) {
86 | params_.insert(*it);
87 | }
88 | }
89 |
90 | maxIter = params_["max iterations"];
91 | epsilon = params_["stopping threshold"];
92 | delta = params_["delta"];
93 | }
94 |
95 | const Eigen::MatrixXd& LieGaussNewton::covariance() {
96 | if (covDirty_) {
97 | covariance_ = covariance_.lu().inverse();
98 | covDirty_ = false;
99 | }
100 |
101 | return covariance_;
102 | }
103 |
104 | const Eigen::MatrixXd& LieGaussNewton::information() {
105 | return information_;
106 | }
107 |
108 | double LieGaussNewton::residual() const {
109 | return last_error;
110 | }
111 |
112 | std::string LieGaussNewton::reason(int32_t errorno) const {
113 | if (errorno == -1) return "Maximum number of iterations reached.";
114 | if (errorno == -2) return "Diverging.";
115 |
116 | return "no error";
117 | }
118 |
119 | const Eigen::Matrix4d& LieGaussNewton::pose() const {
120 | return Tk_;
121 | }
122 |
123 | uint32_t LieGaussNewton::iterationCount() const {
124 | return k_;
125 | }
126 |
--------------------------------------------------------------------------------
/src/core/LieGaussNewton.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_CORE_LIEGAUSSNEWTON_H_
2 | #define INCLUDE_CORE_LIEGAUSSNEWTON_H_
3 |
4 | #include
5 | #include "Objective.h"
6 |
7 | #include
8 |
9 | /** \brief Gauss-Newton method on Lie Groups, which performs the update using matrix exponentials.
10 | *
11 | * Based on Gauss Newton method, but using poses and pose increments for representation of state.
12 | * The actual implementation of the pose increments in handled by the objective. Therefore the
13 | * optimizer only produces increments, which are then integrated by the objective.
14 | *
15 | * \author behley
16 | **/
17 |
18 | class OptimizerCallback {
19 | public:
20 | virtual ~OptimizerCallback() {}
21 |
22 | virtual void operator()(float residual, const Eigen::VectorXd& x, const Eigen::Matrix4d& pose) = 0;
23 | };
24 |
25 | class LieGaussNewton {
26 | public:
27 | LieGaussNewton();
28 |
29 | void setParameters(const rv::ParameterList& params);
30 |
31 | /** \brief optimize F starting with given pose T0. **/
32 | int32_t minimize(Objective& F, const Eigen::Matrix4d& T0);
33 |
34 | /** \brief initialize given objective and pose... **/
35 | void initialize(Objective& F, const Eigen::Matrix4d& T0);
36 |
37 | /** \brief perform a single step with the initialized objective. **/
38 | int32_t step();
39 |
40 | /** \brief get last residual. **/
41 | double residual() const;
42 |
43 | /** \brief get best pose. **/
44 | const Eigen::Matrix4d& pose() const;
45 |
46 | std::string reason(int32_t errorno) const;
47 |
48 | const Eigen::MatrixXd& covariance();
49 |
50 | const Eigen::MatrixXd& information();
51 |
52 | uint32_t iterationCount() const;
53 |
54 | // some constants.
55 | static const int32_t CONVERGED{0};
56 |
57 | const std::vector& history() const { return history_; }
58 |
59 | protected:
60 | rv::ParameterList params_;
61 | double last_error{134567.00};
62 | uint32_t k_{0};
63 | Eigen::Matrix4d Tk_;
64 | Eigen::MatrixXd information_;
65 | Eigen::MatrixXd covariance_;
66 | bool covDirty_{true};
67 | Objective* objective_{nullptr};
68 |
69 | Eigen::MatrixXd JtJ, Jtf;
70 |
71 | uint32_t maxIter{200};
72 | double epsilon{1e-10}, delta{1e-10};
73 |
74 | std::vector history_;
75 | std::shared_ptr callback_;
76 | };
77 |
78 | #endif /* INCLUDE_CORE_LIEGAUSSNEWTON_H_ */
79 |
--------------------------------------------------------------------------------
/src/core/Objective.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_CORE_PROJECTIVEICP_H_
2 | #define INCLUDE_CORE_PROJECTIVEICP_H_
3 |
4 | #include "Frame.h"
5 | #include "lie_algebra.h"
6 |
7 | #include
8 |
9 | #include
10 |
11 | class Objective {
12 | public:
13 | virtual ~Objective() {}
14 |
15 | virtual uint32_t num_parameters() const = 0;
16 |
17 | /** \brief set single parameter to specific value. **/
18 | virtual void setParameter(const rv::Parameter& param) {}
19 |
20 | /** \brief Current and last frame.
21 | *
22 | * \param current data from current point cloud (the so-called "live" frame)
23 | * \param last date from previous point cloud or data generated from model. (the so-called "model" frame)
24 | *
25 | * At least one is implemented by the derived classes.
26 | **/
27 | virtual void setData(const std::shared_ptr& current, const std::shared_ptr& last) {
28 | throw std::runtime_error("ProjectiveICP::setData not implemented.");
29 | }
30 |
31 | /** \brief compute residual with given increment.
32 | *
33 | * Beware: naive implementation using jacobianProducts; should be overwritten by derived
34 | * classes for maximal performance
35 | */
36 | virtual double residual(const Eigen::VectorXd& delta) = 0;
37 |
38 | /** \brief compute JtJ and Jtf exploiting intermediate computations at current pose, return F(x). */
39 | virtual double jacobianProducts(Eigen::MatrixXd& JtJ, Eigen::MatrixXd& Jtf) = 0;
40 |
41 | /** \brief increment by given delta. **/
42 | void increment(const Eigen::VectorXd& delta) {
43 | pose_ = SE3::exp(delta) * pose_;
44 | iteration_ += 1;
45 | }
46 |
47 | uint32_t inlier() const { return inlier_; }
48 | uint32_t outlier() const { return outlier_; }
49 | uint32_t valid() const { return (inlier_ + outlier_); }
50 | uint32_t invalid() const { return invalid_; }
51 |
52 | float inlier_residual() const { return inlier_residual_; }
53 |
54 | /** \brief initialize the pose estimate. **/
55 | void initialize(const Eigen::Matrix4d& T0) { pose_ = T0; }
56 |
57 | const Eigen::Matrix4d& pose() const { return pose_; }
58 |
59 | /** \brief specify at which level the redisual/jacobian is computed. **/
60 | virtual void setLevel(uint32_t lvl) { // noop
61 | }
62 |
63 | /** \brief get maximum level (coarse-to-fine). **/
64 | virtual uint32_t getMaxLevel() const { return 0; }
65 |
66 | virtual void reset() { /* noop */
67 | }
68 |
69 | protected:
70 | // can only be instanced by derived classes.
71 | Objective() {}
72 |
73 | Eigen::Matrix4d pose_{Eigen::Matrix4d::Identity()};
74 | uint32_t iteration_{0};
75 | uint32_t inlier_{0};
76 | float inlier_residual_{0.0f};
77 | uint32_t outlier_{0};
78 | uint32_t invalid_{0};
79 | };
80 |
81 | #endif /* INCLUDE_CORE_PROJECTIVEICP_H_ */
82 |
--------------------------------------------------------------------------------
/src/core/Posegraph.cpp:
--------------------------------------------------------------------------------
1 | #include "Posegraph.h"
2 |
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | using namespace gtsam;
12 |
13 | Posegraph::Posegraph() {}
14 |
15 | void Posegraph::clear() {
16 | graph_ = NonlinearFactorGraph();
17 | initial_.clear();
18 | edges_.clear();
19 | result_.clear();
20 | }
21 |
22 | Posegraph::Ptr Posegraph::clone() const {
23 | return Posegraph::Ptr(new Posegraph(*this));
24 | }
25 |
26 | void Posegraph::setInitial(int32_t id, const Eigen::Matrix4d& initial_estimate) {
27 | bool addPrior = initial_.empty();
28 |
29 | // std::cout << "Initializing " << id << std::endl;
30 |
31 | if (!initial_.exists(id))
32 | initial_.insert(id, Pose3(initial_estimate));
33 | else
34 | initial_.update(id, Pose3(initial_estimate));
35 |
36 | // also update intermediate result.
37 | if (result_.exists(id))
38 | result_.update(id, Pose3(initial_estimate));
39 | else
40 | result_.insert(id, Pose3(initial_estimate));
41 |
42 | if (addPrior) {
43 | Vector6 diagonal;
44 | diagonal << 1e-6, 1e-6, 1e-6, 1e-6, 1e-6, 1e-6;
45 | noiseModel::Diagonal::shared_ptr priorModel = noiseModel::Diagonal::Variances(diagonal);
46 | graph_.add(PriorFactor(id, Pose3(), priorModel));
47 | }
48 | }
49 |
50 | void Posegraph::addEdge(int32_t from, int32_t to, const Eigen::Matrix4d& measurement,
51 | const Matrix6d& information) {
52 | SharedNoiseModel model = noiseModel::Gaussian::Information(information);
53 | if (robustify_) {
54 | model = noiseModel::Robust::Create(robustifier_, model);
55 | }
56 |
57 | NonlinearFactor::shared_ptr factor(new BetweenFactor(from, to, Pose3(measurement), model));
58 | graph_.add(factor);
59 |
60 | edges_.push_back({from, to, measurement});
61 | }
62 |
63 | Eigen::Matrix4d Posegraph::pose(int32_t id) const {
64 | return result_.at(id).matrix();
65 | }
66 |
67 | std::vector Posegraph::poses() const {
68 | std::vector poses(result_.size());
69 |
70 | for (const auto& pair : result_) {
71 | if (pair.key >= poses.size()) continue;
72 |
73 | poses[pair.key] = pair.value.cast().matrix();
74 | }
75 |
76 | return poses;
77 | }
78 |
79 | int32_t Posegraph::size() const {
80 | return result_.size();
81 | }
82 |
83 | void Posegraph::reinitialize() {
84 | result_ = initial_;
85 | }
86 |
87 | double Posegraph::error() const {
88 | return graph_.error(result_);
89 | }
90 |
91 | bool Posegraph::optimize(uint32_t num_iters) {
92 | // todo: check if every value has been initialized.
93 |
94 | LevenbergMarquardtParams params;
95 | params.maxIterations = num_iters;
96 | params.setVerbosity("TERMINATION"); // this will show info about stopping conditions
97 | params.setVerbosity("SILENT");
98 | LevenbergMarquardtOptimizer optimizer(graph_, result_, params);
99 |
100 | // std::cout << ">> initial error = " << graph_.error(result_) << std::endl;
101 | result_ = optimizer.optimize();
102 | // std::cout << ">> final error = " << graph_.error(result_) << std::endl;
103 |
104 | return true;
105 | }
106 |
107 | const gtsam::NonlinearFactorGraph& Posegraph::graph() const {
108 | return graph_;
109 | }
110 |
111 | const gtsam::Values& Posegraph::initial() const {
112 | return initial_;
113 | }
114 |
115 | const gtsam::Values& Posegraph::result() const {
116 | return result_;
117 | }
118 |
119 | void Posegraph::save(const std::string& filename) const {}
120 | void Posegraph::load(const std::string& filename) {}
121 |
122 | void Posegraph::setMEstimator(const gtsam::noiseModel::mEstimator::Base::shared_ptr& m_estimator) {
123 | robustify_ = (m_estimator != nullptr);
124 | robustifier_ = m_estimator;
125 | }
126 |
--------------------------------------------------------------------------------
/src/core/Posegraph.h:
--------------------------------------------------------------------------------
1 | #ifndef SRC_CORE_POSEGRAPH_H_
2 | #define SRC_CORE_POSEGRAPH_H_
3 |
4 | #include
5 | #include
6 |
7 | class Posegraph {
8 | public:
9 | typedef std::shared_ptr Ptr;
10 | typedef std::shared_ptr ConstPtr;
11 | typedef Eigen::Matrix Matrix6d;
12 | typedef Eigen::Matrix Matrix6f;
13 |
14 | class Edge {
15 | public:
16 | Edge(int32_t from_, int32_t to_, const Eigen::Matrix4d& measurement_)
17 | : from(from_), to(to_), measurement(measurement_) {}
18 | int32_t from, to;
19 | Eigen::Matrix4d measurement;
20 | };
21 |
22 | Posegraph();
23 |
24 | Posegraph::Ptr clone() const;
25 |
26 | double error() const;
27 |
28 | void clear();
29 |
30 | /** \brief add estimate for a given node \id. **/
31 | void setInitial(int32_t id, const Eigen::Matrix4d& initial_estimate);
32 |
33 | /** \brief insert edge from node \from to node \to with given measurement (SE3 transform) and information matrix.
34 | *
35 | * The information matrix is the inverse of the covariance, where the
36 | **/
37 | void addEdge(int32_t from, int32_t to, const Eigen::Matrix4d& measurement, const Matrix6d& information);
38 |
39 | /** \brief get pose for given id. **/
40 | Eigen::Matrix4d pose(int32_t id) const;
41 |
42 | /** \brief get (optimized) poses sorted by id of the vertexes. **/
43 | std::vector poses() const;
44 |
45 | /** \brief size of graph, i.e., number of vertices **/
46 | int32_t size() const;
47 |
48 | /** \brief reinitialize model with initial estimates. **/
49 | void reinitialize();
50 |
51 | /** \brief start optimization for given number of iterations. **/
52 | bool optimize(uint32_t num_iters);
53 |
54 | void save(const std::string& filename) const;
55 | void load(const std::string& filename);
56 |
57 | /** \brief set m estimator. **/
58 | void setMEstimator(const gtsam::noiseModel::mEstimator::Base::shared_ptr& m_estimator);
59 |
60 | const gtsam::NonlinearFactorGraph& graph() const;
61 | const gtsam::Values& initial() const;
62 | const gtsam::Values& result() const;
63 |
64 | const std::vector& getEdges() const { return edges_; }
65 |
66 | protected:
67 | std::vector edges_;
68 | gtsam::Values initial_;
69 | gtsam::Values result_;
70 | gtsam::NonlinearFactorGraph graph_;
71 |
72 | bool robustify_{false};
73 | gtsam::noiseModel::mEstimator::Base::shared_ptr robustifier_;
74 | };
75 |
76 | #endif /* SRC_CORE_POSEGRAPH_H_ */
77 |
--------------------------------------------------------------------------------
/src/core/Preprocessing.h:
--------------------------------------------------------------------------------
1 | #ifndef SRC_CORE_PREPROCESSING_H_
2 | #define SRC_CORE_PREPROCESSING_H_
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include "core/Frame.h"
15 |
16 | /** \brief Preprocessing of the data.
17 | *
18 | * We essentially take the point cloud, convert it to polar coordinates and generate from
19 | * this a vertex map. We furthermore
20 | *
21 | * Point cloud's coordinate system should be right-handed coordinate system: x - forward, y - left, z - up.
22 | * The generated vertex map and normal map has normalized yaw/azimuth angles on the x-axis(right) and the normalized
23 | * pitch angles on the y-axis (up). Yaw angles ranges from [-pi, pi], where 0 is in the middle of the image.
24 | * The pitch angle ranges from [0, |fov_up| + |fov_down|] and starts at the upper border of the depth image.
25 | *
26 | * the coordinate system therefore looks something like this:
27 | * ---------------------------
28 | * |<--- -yaw- (0,0) -+yaw-->|
29 | * y | |
30 | * | v |
31 | * O--x-->--------------------
32 | *
33 | * The vertex map and normal map are represented by an OpenGl texture and therefore have their origin in the lower
34 | *left.
35 | *
36 | * The vertex map and normal map store in the fourth coordinate if the point is valid. If the vertex or
37 | * normal is valid, the fourth coordinate is 1; otherwise 0.
38 | *
39 | * \author behley
40 | **/
41 |
42 | class Preprocessing {
43 | public:
44 | Preprocessing(const rv::ParameterList& params);
45 |
46 | void setParameters(const rv::ParameterList& params);
47 |
48 | /** \brief pre-process the point cloud (vertex map, normal map, ... etc.) and store results in frame. **/
49 | void process(glow::GlBuffer& points, Frame& frame);
50 |
51 | protected:
52 | uint32_t width_, height_;
53 | glow::GlProgram depth_program_, normal_program_, bilateral_program_, avg_program_;
54 | glow::GlVertexArray vao_points_; // laser points
55 | glow::GlVertexArray vao_no_points_; // no points.
56 | glow::GlVertexArray vao_img_verts_; // for each image coordinate, we have an point.
57 | glow::GlSampler sampler_;
58 | glow::GlFramebuffer framebuffer_;
59 | glow::GlTextureRectangle temp_vertices_;
60 |
61 | bool filterVertexmap_{false};
62 | bool useFilteredVertexmap_{true};
63 | bool avgVertexmap_{false};
64 | };
65 |
66 | #endif /* SRC_CORE_PREPROCESSING_H_ */
67 |
--------------------------------------------------------------------------------
/src/core/Surfel.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_CORE_SURFEL_H_
2 | #define INCLUDE_CORE_SURFEL_H_
3 |
4 | /** \brief surfel representation. **/
5 | struct Surfel {
6 | public:
7 | float x, y, z;
8 | float radius;
9 | float nx, ny, nz;
10 | float confidence;
11 |
12 | int32_t timestamp;
13 | float color, weight, count;
14 | };
15 |
16 | #endif /* INCLUDE_CORE_SURFEL_H_ */
17 |
--------------------------------------------------------------------------------
/src/core/lie_algebra.cpp:
--------------------------------------------------------------------------------
1 | #include "lie_algebra.h"
2 | #include
3 |
4 | Eigen::Matrix4d SE3::exp(const Eigen::VectorXd& x) {
5 | // std::cout << "exponential of " << x.transpose() << std::endl;
6 | Eigen::Matrix4d result = Eigen::Matrix4d::Identity();
7 |
8 | // see phd thesis of Hauke Strasdat, 2012 pp 47-53:
9 | Eigen::Vector3d v(x[0], x[1], x[2]);
10 | Eigen::Vector3d omega(x[3], x[4], x[5]);
11 |
12 | double theta = omega.norm();
13 | if (theta > 1e-10) {
14 | Eigen::Matrix3d omega_skew;
15 | omega_skew << 0, -omega[2], omega[1], omega[2], 0, -omega[0], -omega[1], omega[0], 0;
16 | Eigen::Matrix3d omega_skew_sqr = omega_skew * omega_skew;
17 |
18 | // Rotation:
19 | double alpha = sin(theta) / theta;
20 | double beta = (1 - cos(theta)) / (theta * theta);
21 | result.topLeftCorner(3, 3) = Eigen::Matrix3d::Identity() + alpha * omega_skew + beta * omega_skew_sqr;
22 |
23 | // Translation:
24 | double gamma = (1.0f - cos(theta)) / (theta * theta);
25 | double delta = (theta - sin(theta)) / (theta * theta * theta);
26 |
27 | Eigen::Matrix3d V = Eigen::Matrix3d::Identity() + gamma * omega_skew + delta * omega_skew_sqr;
28 | result.block<3, 1>(0, 3) = V * v;
29 | } else {
30 | result.block<3, 1>(0, 3) = v; // just translation.
31 | }
32 |
33 | return result;
34 | }
35 |
36 | Eigen::VectorXd SE3::log(const Eigen::Matrix4d& M) {
37 | // see PhD thesis of Hauke Strasdat, 2012 pp. 47-53 and Sophus library:
38 |
39 | Eigen::VectorXd x = Eigen::VectorXd::Zero(6);
40 |
41 | Eigen::Matrix3d R = M.topLeftCorner(3, 3);
42 | Eigen::Vector3d t = M.topRightCorner(3, 1);
43 |
44 | double d = 0.5 * (R.trace() - 1.0);
45 | Eigen::Matrix3d omega_skew = Eigen::Matrix3d::Zero();
46 |
47 | if (d < 1 - 1e-10) {
48 | double theta = std::acos(d);
49 | omega_skew = theta / (2 * std::sin(theta)) * (R - R.transpose());
50 |
51 | x[3] = omega_skew(2, 1);
52 | x[4] = omega_skew(0, 2);
53 | x[5] = omega_skew(1, 0);
54 | }
55 |
56 | double theta = x.tail(3).norm();
57 |
58 | x.head(3) = t;
59 |
60 | if (std::abs(theta) > 1e-10) {
61 | double half_theta = 0.5 * theta;
62 |
63 | double alpha = -0.5;
64 | double beta = 1 / (theta * theta) * (1 - theta * std::cos(half_theta) / (2 * std::sin(half_theta)));
65 | Eigen::Matrix3d V_inv = Eigen::Matrix3d::Identity() + alpha * omega_skew + beta * (omega_skew * omega_skew);
66 |
67 | x.head(3) = V_inv * t;
68 | }
69 |
70 | return x;
71 | }
72 |
73 | Eigen::Matrix4d SO3::exp(const Eigen::VectorXd& x) {
74 | Eigen::Matrix4d result = Eigen::Matrix4d::Identity();
75 |
76 | Eigen::Vector3d omega(x[0], x[1], x[2]);
77 |
78 | double theta = omega.norm();
79 | if (theta > 1e-10) {
80 | Eigen::Matrix3d omega_skew;
81 | omega_skew << 0, -omega[2], omega[1], omega[2], 0, -omega[0], -omega[1], omega[0], 0;
82 | Eigen::Matrix3d omega_skew_sqr = omega_skew * omega_skew;
83 | result.topLeftCorner(3, 3) = Eigen::Matrix3d::Identity() + sin(theta) / theta * omega_skew +
84 | (1 - cos(theta)) / (theta * theta) * omega_skew_sqr;
85 | }
86 |
87 | return result;
88 | }
89 |
--------------------------------------------------------------------------------
/src/core/lie_algebra.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_CORE_LIE_ALGEBRA_H_
2 | #define INCLUDE_CORE_LIE_ALGEBRA_H_
3 |
4 | #include
5 |
6 | /** \brief some utility functions for Lie groups.
7 | *
8 | * \author behley
9 | **/
10 |
11 | class SE3 {
12 | public:
13 | SE3() = delete;
14 |
15 | /** \brief get rotation matrix from angle-axis + translation **/
16 | static Eigen::Matrix4d exp(const Eigen::VectorXd& x);
17 |
18 | /** \brief get angle-axis + translation from rotation matrix **/
19 | static Eigen::VectorXd log(const Eigen::Matrix4d& x);
20 | };
21 |
22 | class SO3 {
23 | public:
24 | SO3() = delete;
25 | static Eigen::Matrix4d exp(const Eigen::VectorXd& x);
26 | };
27 |
28 | #endif /* INCLUDE_CORE_LIE_ALGEBRA_H_ */
29 |
--------------------------------------------------------------------------------
/src/io/KITTIReader.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef KITTILASERSCANREADER_H_
3 | #define KITTILASERSCANREADER_H_
4 |
5 | #include "LaserscanReader.h"
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | namespace rv {
12 |
13 | /** \brief a reader for the KITTI datasets provided by the KIT.
14 | *
15 | * \author behley
16 | */
17 | class KITTIReader : public LaserscanReader {
18 | public:
19 | KITTIReader(const std::string& scan_filename, uint32_t buffer_size = 50);
20 |
21 | void reset() override;
22 | bool read(Laserscan& scan) override;
23 | void seek(uint32_t scan) override;
24 | bool isSeekable() const override;
25 | uint32_t count() const override;
26 |
27 | protected:
28 | void initScanFilenames(const std::string& scan_filename);
29 |
30 | bool read(uint32_t scan_idx, Laserscan& scan);
31 |
32 | int32_t currentScan;
33 | std::vector scan_filenames;
34 | RingBuffer bufferedScans;
35 | uint32_t firstBufferedScan;
36 |
37 | };
38 | }
39 | #endif /* KITTILASERSCANREADER_H_ */
40 |
--------------------------------------------------------------------------------
/src/io/LaserscanReader.h:
--------------------------------------------------------------------------------
1 | #ifndef LASERSCANREADER_H_
2 | #define LASERSCANREADER_H_
3 |
4 | #include
5 |
6 | #include
7 | #include
8 |
9 | namespace rv {
10 |
11 | /** \brief laserscan reader interface
12 | *
13 | * Defines the interface for a (seekable) laserscan reader.
14 | *
15 | * A seekable laser scan should reimplement the seek method.
16 | *
17 | * \author behley
18 | **/
19 | class LaserscanReader {
20 | public:
21 | virtual ~LaserscanReader() {}
22 |
23 | /** \brief reset laser scan reader. **/
24 | virtual void reset() = 0;
25 |
26 | /** \brief read next laser range scan **/
27 | virtual bool read(Laserscan& scan) = 0;
28 |
29 | /** \brief is reader seekable? **/
30 | virtual bool isSeekable() const { return false; }
31 |
32 | /** \brief seek to a specific scan number. **/
33 | virtual void seek(uint32_t scan) { throw IOError("Laserscan reader not seekable."); }
34 |
35 | /** \brief number of scans in the logfile **/
36 | virtual uint32_t count() const = 0;
37 | };
38 | }
39 | #endif /* LASERSCANREADER_H_ */
40 |
--------------------------------------------------------------------------------
/src/io/RobocarReader.cpp:
--------------------------------------------------------------------------------
1 | #include "RobocarReader.h"
2 |
3 | #include
4 | #include
5 |
6 | namespace rv
7 | {
8 |
9 | RobocarReader::RobocarReader(const std::string& filename) {
10 | std::vector files = FileUtil::getDirectoryListing(FileUtil::dirName(filename));
11 |
12 | for (uint32_t i = 0; i < files.size(); ++i) {
13 | if (FileUtil::extension(files[i]) == ".bin") {
14 | filenames_.push_back(files[i]);
15 | }
16 | }
17 | std::cout << "Found " << filenames_.size() << " scans." << std::endl;
18 | std::sort(filenames_.begin(), filenames_.end());
19 | }
20 |
21 | void RobocarReader::reset() {
22 | currentIdx_ = 0;
23 | }
24 |
25 | bool RobocarReader::read(Laserscan& scan) {
26 | if (currentIdx_ >= filenames_.size()) return false;
27 |
28 | std::ifstream in(filenames_[currentIdx_].c_str(), std::ios::binary);
29 | if (!in.is_open()) return false;
30 |
31 | scan.clear();
32 |
33 | in.seekg(0, std::ios::end);
34 | uint32_t num_points = in.tellg() / (3 * sizeof(double));
35 | in.seekg(0, std::ios::beg);
36 |
37 | std::vector values(3 * num_points);
38 | in.read((char*)&values[0], 3 * num_points * sizeof(double));
39 |
40 | in.close();
41 | std::vector& points = scan.points();
42 |
43 | points.resize(num_points);
44 |
45 | for (uint32_t i = 0; i < num_points; ++i) {
46 | points[i].x() = values[3 * i];
47 | points[i].y() = -values[3 * i + 1];
48 | points[i].z() = -values[3 * i + 2];
49 | }
50 |
51 | currentIdx_ += 1;
52 |
53 | return true;
54 | }
55 |
56 | bool RobocarReader::isSeekable() const {
57 | return true;
58 | }
59 |
60 | void RobocarReader::seek(uint32_t scan) {
61 | currentIdx_ = scan;
62 | }
63 |
64 | uint32_t RobocarReader::count() const {
65 | return filenames_.size();
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/io/RobocarReader.h:
--------------------------------------------------------------------------------
1 | #ifndef SRC_IO_ROBOCARREADER_H_
2 | #define SRC_IO_ROBOCARREADER_H_
3 |
4 | #include "LaserscanReader.h"
5 |
6 | namespace rv {
7 |
8 | /** \brief Laserscan reader for RoboCar dataset.
9 | *
10 | * \author behley
11 | **/
12 | class RobocarReader : public LaserscanReader {
13 | public:
14 | RobocarReader(const std::string& filename);
15 |
16 | void reset();
17 |
18 | bool read(rv::Laserscan& scan);
19 |
20 | bool isSeekable() const;
21 |
22 | void seek(uint32_t scan);
23 |
24 | uint32_t count() const;
25 |
26 | protected:
27 | uint32_t currentIdx_{0};
28 | std::vector filenames_;
29 | };
30 | }
31 |
32 | #endif /* SRC_IO_ROBOCARREADER_H_ */
33 |
--------------------------------------------------------------------------------
/src/io/SimulationReader.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_CORE_SIMULATIONREADER_H_
2 | #define INCLUDE_CORE_SIMULATIONREADER_H_
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | /** \brief simple generator of simulated laser scan data. **/
9 | class SimulationReader : public rv::LaserscanReader {
10 | public:
11 | SimulationReader(const std::string& filename);
12 |
13 | void reset();
14 |
15 | bool read(rv::Laserscan& scan);
16 |
17 | bool isSeekable() const;
18 |
19 | void seek(uint32_t scan);
20 |
21 | uint32_t count() const;
22 |
23 | const std::vector& getTrajectory() const;
24 |
25 | void setParameters(const rv::ParameterList& params);
26 |
27 | protected:
28 | class Ray {
29 | public:
30 | Ray(const Eigen::Vector4f& origin, const Eigen::Vector4f& dir);
31 | Eigen::Vector4f operator()(float t) const;
32 |
33 | Eigen::Vector4f o;
34 | Eigen::Vector4f d;
35 | };
36 |
37 | class Triangle {
38 | public:
39 | Triangle(const Eigen::Vector4f& p1, const Eigen::Vector4f& p2, const Eigen::Vector4f& p3);
40 |
41 | Eigen::Vector4f vertices[3]; // needed to determine if inside plane.
42 | };
43 |
44 | /** \brief intersect ray with triangle. if true, than t is the parameter needed to calculate the intersection from
45 | * ray. **/
46 | bool intersect(const Ray& ray, const Triangle& triangle, float& t);
47 |
48 | void addCube(const Eigen::Matrix4f& pose, float size);
49 | void addPlane(const Eigen::Matrix4f& pose, float size);
50 |
51 | uint32_t timestamp_{0};
52 | std::vector trajectory_; // sensor locations at time t.
53 | std::vector world_; // world is described by triangles.
54 | std::vector buffer_;
55 |
56 | std::vector beams_;
57 | std::vector calibration_;
58 | Eigen::Matrix4f extrinsicPose_;
59 |
60 | rv::Random rng_;
61 | float sigma_noise_{0.0f};
62 | float sigma_speed_{0.0f};
63 | };
64 |
65 | #endif /* INCLUDE_CORE_SIMULATIONREADER_H_ */
66 |
--------------------------------------------------------------------------------
/src/opengl/MatrixStack.h:
--------------------------------------------------------------------------------
1 | /*
2 | * MatrixStack.h
3 | *
4 | * Created on: Jun 28, 2016
5 | * Author: behley
6 | */
7 |
8 | #ifndef INCLUDE_CORE_MATRIXSTACK_H_
9 | #define INCLUDE_CORE_MATRIXSTACK_H_
10 |
11 | #include
12 |
13 | /** \brief All matrices needed for correct rendering and shading.
14 | *
15 | * For correct (phong) shading, we have to determine the angles between model
16 | * normals and the light directions. Therefore, we have to first transform all
17 | * model vertices in world coordinates and also transform the normals. Thus, it
18 | * is handy to not only have the model-view-projection matrix, but also the model
19 | * matrix and the normal matrix to apply local transformations.
20 | *
21 | * \author behley
22 | */
23 | struct MatrixStack {
24 | public:
25 | Eigen::Matrix4f mvp; // model-view-projection matrix.
26 | Eigen::Matrix4f model; // model matrix for transformation in world coordinates
27 | Eigen::Matrix4f normal; // normal matrix for transformation in world coordinates.
28 | };
29 |
30 | #endif /* INCLUDE_CORE_MATRIXSTACK_H_ */
31 |
--------------------------------------------------------------------------------
/src/opengl/Mesh.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_CORE_MESH_H_
2 | #define INCLUDE_CORE_MESH_H_
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 |
10 | class Model;
11 |
12 | /** \brief Representation of a mesh with possibly multiple materials.
13 | *
14 | * A mesh contains all vertex buffers for drawing.
15 | *
16 | * \author behley
17 | */
18 | class Mesh {
19 | friend class Model;
20 |
21 | public:
22 | struct Material {
23 | public:
24 | Material() : ambient(0.1, 0.1, 0.1), diffuse(0.0, 0.0, 0.9), specular(0, 0, 0), emission(0, 0, 0) {}
25 | glow::vec3 ambient;
26 | glow::vec3 diffuse;
27 | glow::vec3 specular;
28 | glow::vec3 emission;
29 |
30 | float shininess{1.0f}; // specular exponent
31 | float alpha{1.0f};
32 | };
33 |
34 | struct Vertex {
35 | public:
36 | glow::vec4 position;
37 | glow::vec4 normal;
38 | glow::vec2 texture;
39 | };
40 |
41 | struct Triangle {
42 | public:
43 | uint32_t vertices[3];
44 | uint32_t material{0};
45 | };
46 |
47 | Mesh();
48 | Mesh(const std::vector& vertices, const std::vector& faces);
49 | Mesh(const std::vector& vertices, const std::vector& faces, const std::vector& materials);
50 |
51 | void draw(glow::GlProgram& program) const;
52 |
53 | const Material& material(uint32_t idx) const {
54 | if (idx >= materials_.size()) throw std::runtime_error("non-existent material.");
55 | return materials_[idx].value();
56 | }
57 |
58 | protected:
59 | void initialize(const std::vector& vertices, const std::vector& faces,
60 | const std::vector& materials);
61 |
62 | float length(const glow::vec4& v);
63 |
64 | glow::GlBuffer vertices_{glow::BufferTarget::ARRAY_BUFFER, glow::BufferUsage::STATIC_DRAW};
65 | std::vector vaos_;
66 | std::vector > triangles_; // per material triangles.
67 | std::vector > materials_;
68 |
69 | // std::vector vertices_;
70 | // std::vector > ;
71 | };
72 |
73 | #endif /* INCLUDE_CORE_MESH_H_ */
74 |
--------------------------------------------------------------------------------
/src/opengl/Model.cpp:
--------------------------------------------------------------------------------
1 | #include "Model.h"
2 |
3 | using namespace glow;
4 |
5 | void Model::attach(const Mesh& mesh) {
6 | model_matrices_.push_back(Eigen::Matrix4f::Identity());
7 | normal_matrices_.push_back(Eigen::Matrix4f::Identity());
8 | meshes_.push_back(mesh);
9 | }
10 |
11 | void Model::attach(const Eigen::Matrix4f& transform, const Mesh& mesh) {
12 | model_matrices_.push_back(transform);
13 | normal_matrices_.push_back(transform.inverse().transpose());
14 | meshes_.push_back(mesh);
15 | }
16 |
17 | void Model::draw(glow::GlProgram& program, const MatrixStack& matrices) const {
18 | GlUniform mvp("mvp", matrices.mvp);
19 | GlUniform model_mat("model_mat", matrices.model);
20 | GlUniform normal_mat("normal_mat", matrices.normal);
21 |
22 | // glEnable(GL_BLEND);
23 | // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
24 | for (uint32_t i = 0; i < meshes_.size(); ++i) {
25 | // apply local transformations to matrix stack.
26 | mvp = matrices.mvp * model_matrices_[i]; // mvp = p * v * m * l
27 | model_mat = matrices.model * model_matrices_[i];
28 | normal_mat = matrices.normal * normal_matrices_[i];
29 |
30 | program.setUniform(mvp);
31 | program.setUniform(model_mat);
32 | program.setUniform(normal_mat);
33 |
34 | meshes_[i].draw(program);
35 | }
36 |
37 | // glDisable(GL_BLEND);
38 | }
39 |
--------------------------------------------------------------------------------
/src/opengl/Model.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_CORE_MODEL_H_
2 | #define INCLUDE_CORE_MODEL_H_
3 |
4 | #include "Mesh.h"
5 | #include "MatrixStack.h"
6 |
7 | /** \brief a model represents a collection of meshes.
8 | *
9 | * \author behley
10 | */
11 | class Model {
12 | public:
13 | /** \brief attach a mesh to the model. **/
14 | void attach(const Mesh& mesh);
15 |
16 | /** \brief attach a mesh to the model with given local transformation. **/
17 | void attach(const Eigen::Matrix4f& transform, const Mesh& mesh);
18 |
19 | /** \brief draw meshes with given program... **/
20 | void draw(glow::GlProgram& program, const MatrixStack& matrices) const;
21 |
22 | protected:
23 | std::vector model_matrices_;
24 | std::vector normal_matrices_;
25 |
26 | std::vector meshes_;
27 | };
28 |
29 | #endif /* INCLUDE_CORE_MODEL_H_ */
30 |
--------------------------------------------------------------------------------
/src/opengl/ObjReader.h:
--------------------------------------------------------------------------------
1 | #ifndef INCLUDE_CORE_OBJREADER_H_
2 | #define INCLUDE_CORE_OBJREADER_H_
3 |
4 | #include "Mesh.h"
5 |
6 | /** \brief reading and parsing of Wavefront .obj files.
7 | *
8 | * \author behley
9 | */
10 | class ObjReader {
11 | public:
12 | /** \brief read mesh from file with given filename. **/
13 | static Mesh fromFile(const std::string& filename);
14 |
15 | protected:
16 | static void parseMaterials(const std::string& filename, std::map& materials);
17 | };
18 |
19 | #endif /* INCLUDE_CORE_OBJREADER_H_ */
20 |
--------------------------------------------------------------------------------
/src/rv/CompositeParameter.cpp:
--------------------------------------------------------------------------------
1 | #include "rv/CompositeParameter.h"
2 | #include
3 |
4 | namespace rv
5 | {
6 |
7 | CompositeParameter::CompositeParameter(const std::string& name) :
8 | Parameter(name)
9 | {
10 |
11 | }
12 |
13 | CompositeParameter::CompositeParameter(const std::string& name, const ParameterList& values) :
14 | Parameter(name), param_list(values)
15 | {
16 |
17 | }
18 |
19 | CompositeParameter::CompositeParameter(const std::string& name, const XmlNode& node) :
20 | Parameter(name), val(node)
21 | {
22 | const XmlNode::List& children = node.getChildren();
23 |
24 | for (XmlNode::List::const_iterator it = children.begin(); it != children.end(); ++it)
25 | {
26 | if (it->getName() == "param")
27 | {
28 | Parameter* p = parseParameter(*it);
29 | if (param_list.hasParam(p->name())) std::cout << "WARNING: (" << name << ") parameter with name = '" << p->name()
30 | << "' already inserted." << std::endl;
31 | param_list.insert(*p);
32 | delete p;
33 | }
34 | }
35 | }
36 |
37 | CompositeParameter::~CompositeParameter()
38 | {
39 |
40 | }
41 |
42 | CompositeParameter::CompositeParameter(const CompositeParameter& other) :
43 | Parameter(other), param_list(other.param_list), val(other.val)
44 | {
45 |
46 | }
47 |
48 | CompositeParameter& CompositeParameter::operator=(const CompositeParameter& other)
49 | {
50 | if (&other == this) return *this;
51 |
52 | Parameter::operator=(other);
53 |
54 | param_list = other.param_list;
55 |
56 | return *this;
57 | }
58 |
59 | CompositeParameter* CompositeParameter::clone() const
60 | {
61 | return new CompositeParameter(*this);
62 | }
63 |
64 | std::string CompositeParameter::toString() const
65 | {
66 | std::stringstream out;
67 |
68 | out << "" << std::endl;
69 | for (ParameterList::const_iterator it = param_list.begin(); it != param_list.end(); ++it)
70 | out << " " << *it << std::endl;
71 | out << "";
72 |
73 | return out.str();
74 | }
75 |
76 | CompositeParameter::operator ParameterList() const
77 | {
78 | return param_list;
79 | }
80 |
81 | CompositeParameter* parseCompositeParameter(const XmlNode& node)
82 | {
83 | return new CompositeParameter(node.getAttribute("name"), node);
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/src/rv/CompositeParameter.h:
--------------------------------------------------------------------------------
1 | /*****************************************************************************\
2 | \brief Parameter as a composition of other Parameters.
3 |
4 | \headerfile CompositeParameter.h
5 | \author behley
6 |
7 | \*****************************************************************************/
8 |
9 | #ifndef COMPOSITEPARAMETER_H_
10 | #define COMPOSITEPARAMETER_H_
11 |
12 | #include "Parameter.h"
13 | #include "ParameterList.h"
14 |
15 | namespace rv
16 | {
17 |
18 | class CompositeParameter: public Parameter
19 | {
20 | public:
21 | CompositeParameter(const std::string& name);
22 | CompositeParameter(const std::string& name, const XmlNode& value);
23 | CompositeParameter(const std::string& name, const ParameterList& values);
24 |
25 | CompositeParameter(const CompositeParameter& other);
26 | virtual CompositeParameter& operator=(const CompositeParameter& other);
27 |
28 | virtual ~CompositeParameter();
29 |
30 | virtual CompositeParameter* clone() const;
31 |
32 | virtual operator ParameterList() const;
33 |
34 | const ParameterList& getParams() const
35 | {
36 | return param_list;
37 | }
38 |
39 | ParameterList& getParams()
40 | {
41 | return param_list;
42 | }
43 |
44 | virtual std::string toString() const;
45 |
46 | virtual inline XmlNode& value()
47 | {
48 | return val;
49 | }
50 |
51 | std::string type() const
52 | {
53 | return "composite";
54 | }
55 |
56 | virtual bool operator==(const Parameter& other) const
57 | {
58 | const CompositeParameter* param = dynamic_cast(&other);
59 | if(param == 0) return false;
60 |
61 | return (other.name() == mName && param_list == param->param_list);
62 | }
63 |
64 | protected:
65 | ParameterList param_list;
66 | XmlNode val;
67 | };
68 |
69 | CompositeParameter* parseCompositeParameter(const XmlNode& node);
70 |
71 | }
72 | #endif /* COMPOSITEPARAMETER_H_ */
73 |
--------------------------------------------------------------------------------
/src/rv/Exception.h:
--------------------------------------------------------------------------------
1 | #ifndef RV_EXCEPTION_H_
2 | #define RV_EXCEPTION_H_
3 |
4 | #include
5 | #include
6 |
7 | /** \brief Base class for exceptions
8 | * \author behley
9 | */
10 | namespace rv
11 | {
12 |
13 | class Exception: public std::exception
14 | {
15 | public:
16 | Exception(const std::string& msg) :
17 | what_(msg)
18 | {
19 | }
20 |
21 | virtual ~Exception() throw ()
22 | {
23 | }
24 |
25 | /** \brief Returns associated message.
26 | *
27 | * This method is called by the C++ runtime system even if
28 | * the exception aborts the program.
29 | */
30 | virtual const char* what() const throw ()
31 | {
32 | return what_.c_str();
33 | }
34 |
35 | private:
36 | std::string what_;
37 | };
38 |
39 | }
40 |
41 | #endif /* SRC_CORE_RV_EXCEPTION_H_ */
42 |
--------------------------------------------------------------------------------
/src/rv/FileUtil.cpp:
--------------------------------------------------------------------------------
1 | #include "rv/FileUtil.h"
2 |
3 | #include
4 | #include
5 |
6 | namespace rv
7 | {
8 |
9 | FileUtil::FileUtil()
10 | {
11 |
12 | }
13 |
14 | std::string FileUtil::dirName(const std::string& path)
15 | {
16 | boost::filesystem::path p(path);
17 |
18 | return p.parent_path().string();
19 | }
20 |
21 | std::string FileUtil::baseName(const std::string& path)
22 | {
23 | boost::filesystem::path p(path);
24 |
25 | return p.filename().string();
26 | }
27 |
28 | std::string FileUtil::extension(const std::string& path, int32_t level)
29 | {
30 | std::string filename = boost::filesystem::path(path).filename().string();
31 | if (filename == "" || filename == "." || filename == "..") return "";
32 |
33 | std::string ext;
34 | while (level-- > 0)
35 | {
36 | std::string::size_type idx = filename.rfind(".");
37 | if (idx == std::string::npos) break;
38 | ext.insert(0, filename.substr(idx));
39 | filename.resize(idx);
40 | }
41 |
42 | return ext;
43 | }
44 |
45 | std::string FileUtil::stripExtension(const std::string& path, int32_t level)
46 | {
47 | std::string filename = boost::filesystem::path(path).filename().string();
48 | if (filename == "" || filename == "." || filename == "..") return path;
49 |
50 | std::string::size_type path_index = dirName(path).length() + 1;
51 | std::string complete_path = path;
52 |
53 | while (level-- > 0)
54 | {
55 | std::string::size_type idx = complete_path.rfind(".");
56 | if (idx == std::string::npos || idx < path_index) break;
57 | complete_path.resize(idx);
58 | }
59 |
60 | return complete_path;
61 | }
62 |
63 | bool FileUtil::exists(const std::string& filename)
64 | {
65 | return boost::filesystem::exists(filename);
66 | }
67 |
68 | std::vector FileUtil::getDirectoryListing(const std::string& directory)
69 | {
70 | typedef boost::filesystem::directory_iterator diter;
71 |
72 | std::vector listing;
73 | for (diter it = diter(directory); it != diter(); ++it)
74 | listing.push_back(it->path().string());
75 |
76 | return listing;
77 | }
78 |
79 | } /* namespace rv */
80 |
--------------------------------------------------------------------------------
/src/rv/FileUtil.h:
--------------------------------------------------------------------------------
1 | #ifndef CORE_FILEUTIL_H_
2 | #define CORE_FILEUTIL_H_
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | /** \brief Decorator for boost::path.
9 | *
10 | *
11 | * \author behley
12 | */
13 |
14 | namespace rv
15 | {
16 |
17 | class FileUtil
18 | {
19 | public:
20 |
21 | /** \brief Returns the directory components of a file path. */
22 | static std::string dirName(const std::string& path);
23 |
24 | /** \brief Strips the directory components from a file path. */
25 | static std::string baseName(const std::string& path);
26 |
27 | /** \brief Returns the file extension.
28 | * \param[in] path the file name
29 | * \param[in] level maximum number of extension levels. For instance, ".tar.gz" has level 2. */
30 | static std::string extension(const std::string& path, int32_t level = 1);
31 |
32 | /** \brief Removes the file extension.
33 | * \param[in] path the file name
34 | * \param[in] level maximum number of extension levels to be stripped. For instance, ".tar.gz" has level 2. */
35 | static std::string stripExtension(const std::string& path, int32_t level = 1);
36 |
37 | /** \brief Checks if a file exists. */
38 | static bool exists(const std::string& filename);
39 |
40 | /** \brief Get directory listing. **/
41 | static std::vector getDirectoryListing(const std::string& directory);
42 |
43 | protected:
44 | FileUtil();
45 | };
46 |
47 | } /* namespace rv */
48 | #endif /* CORE_FILEUTIL_H_ */
49 |
--------------------------------------------------------------------------------
/src/rv/IOError.h:
--------------------------------------------------------------------------------
1 | #ifndef CORE_IOERROR_H_
2 | #define CORE_IOERROR_H_
3 |
4 | #include
5 |
6 | /** \brief Exception associated with I/O operations
7 | *
8 | * \author behley
9 | */
10 |
11 | namespace rv
12 | {
13 |
14 | class IOError: public std::runtime_error
15 | {
16 | public:
17 | IOError(const std::string& reason)
18 | : std::runtime_error(reason)
19 | {
20 | }
21 | };
22 |
23 | } // namespace rv
24 |
25 | #endif /* SRC_CORE_RV_IOERROR_H_ */
26 |
--------------------------------------------------------------------------------
/src/rv/Laserscan.cpp:
--------------------------------------------------------------------------------
1 | #include "rv/Laserscan.h"
2 |
3 | namespace rv
4 | {
5 |
6 | Laserscan::Laserscan()
7 | {
8 |
9 | }
10 |
11 | Laserscan::Laserscan(const Transform& pose, const std::list& points, const std::list& remissions) :
12 | mPose(pose)
13 | {
14 | points_.assign(points.begin(), points.end());
15 | remissions_.assign(remissions.begin(), remissions.end());
16 | }
17 |
18 | /** \brief clear **/
19 | void Laserscan::clear()
20 | {
21 | points_.clear();
22 | remissions_.clear();
23 | normals_.clear();
24 | }
25 |
26 | /** \brief getter for points/normals/remission **/
27 | const Point3f& Laserscan::point(uint32_t i) const
28 | {
29 | assert(i < points_.size());
30 | return points_[i];
31 | }
32 |
33 | float Laserscan::remission(uint32_t i) const
34 | {
35 | assert(i < remissions_.size());
36 | return remissions_[i];
37 | }
38 |
39 | const Normal3f& Laserscan::normal(uint32_t i) const
40 | {
41 | assert(i < normals_.size());
42 | return normals_[i];
43 | }
44 |
45 | Transform& Laserscan::pose()
46 | {
47 | return mPose;
48 | }
49 |
50 | const Transform& Laserscan::pose() const
51 | {
52 | return mPose;
53 | }
54 |
55 | uint32_t Laserscan::size() const
56 | {
57 | return points_.size();
58 | }
59 |
60 | std::vector& Laserscan::points()
61 | {
62 | return points_;
63 | }
64 |
65 | const std::vector& Laserscan::points() const
66 | {
67 | return points_;
68 | }
69 |
70 | std::vector& Laserscan::remissions()
71 | {
72 | return remissions_;
73 | }
74 |
75 | const std::vector& Laserscan::remissions() const
76 | {
77 | return remissions_;
78 | }
79 |
80 | const std::vector& Laserscan::normals() const
81 | {
82 | return normals_;
83 | }
84 |
85 | std::vector& Laserscan::normals()
86 | {
87 | return normals_;
88 | }
89 |
90 | bool Laserscan::hasRemission() const
91 | {
92 | return (points_.size() == remissions_.size());
93 | }
94 |
95 | bool Laserscan::hasNormals() const
96 | {
97 | return (points_.size() == remissions_.size());
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/rv/Laserscan.h:
--------------------------------------------------------------------------------
1 | /**
2 | * \brief representation of a laser range scan with points + remission and optional normals.
3 | *
4 | * The remission is assumed to give values in [0.0,1.0]
5 | *
6 | * \author behley
7 | */
8 |
9 | #ifndef LASERSCAN_H_
10 | #define LASERSCAN_H_
11 |
12 | #include "geometry.h"
13 | #include "transform.h"
14 | #include
15 | #include
16 | #include
17 |
18 | namespace rv
19 | {
20 |
21 | class Laserscan
22 | {
23 | public:
24 | friend class LaserscanReader;
25 | friend class KITTIReader;
26 | friend class PCAPReader;
27 | friend class EZDReader;
28 | friend class BLFReader;
29 | friend class FreiburgReader;
30 |
31 | Laserscan();
32 | Laserscan(const Transform& pose, const std::list& points,
33 | const std::list& remission);
34 |
35 | /** \brief clear **/
36 | void clear();
37 |
38 | /** \brief getter for points/normals/remission **/
39 | const Point3f& point(uint32_t i) const;
40 | float remission(uint32_t i) const;
41 | const Normal3f& normal(uint32_t) const;
42 |
43 | /** \brief global pose of the sensor. **/
44 | Transform& pose();
45 | const Transform& pose() const;
46 | /** \brief number of points. **/
47 | uint32_t size() const;
48 |
49 | /** access to the raw data. **/
50 | std::vector& points();
51 | const std::vector& points() const;
52 | std::vector& remissions();
53 | const std::vector& remissions() const;
54 | std::vector& normals();
55 | const std::vector& normals() const;
56 |
57 | bool hasRemission() const;
58 | bool hasNormals() const;
59 |
60 | protected:
61 | Transform mPose;
62 | /** representing points, normals, and remission this way, we can separately initialize points and normals **/
63 | std::vector points_;
64 | std::vector remissions_;
65 | std::vector normals_;
66 | };
67 |
68 | }
69 |
70 | #endif /* LASERSCAN_H_ */
71 |
--------------------------------------------------------------------------------
/src/rv/LaserscanReader.h:
--------------------------------------------------------------------------------
1 | #ifndef LASERSCANREADER_H_
2 | #define LASERSCANREADER_H_
3 |
4 | #include
5 |
6 | #include
7 | #include
8 |
9 | namespace rv {
10 |
11 | /** \brief laserscan reader interface
12 | *
13 | * Defines the interface for a (seekable) laserscan reader.
14 | *
15 | * A seekable laser scan should reimplement the seek method.
16 | *
17 | * \author behley
18 | **/
19 | class LaserscanReader {
20 | public:
21 | virtual ~LaserscanReader() {}
22 |
23 | /** \brief reset laser scan reader. **/
24 | virtual void reset() = 0;
25 |
26 | /** \brief read next laser range scan **/
27 | virtual bool read(Laserscan& scan) = 0;
28 |
29 | /** \brief is reader seekable? **/
30 | virtual bool isSeekable() const { return false; }
31 |
32 | /** \brief seek to a specific scan number. **/
33 | virtual void seek(uint32_t scan) { throw IOError("Laserscan reader not seekable."); }
34 |
35 | /** \brief number of scans in the logfile **/
36 | virtual uint32_t count() const = 0;
37 | };
38 | }
39 | #endif /* LASERSCANREADER_H_ */
40 |
--------------------------------------------------------------------------------
/src/rv/Parameter.cpp:
--------------------------------------------------------------------------------
1 | #include "rv/Parameter.h"
2 | #include "rv/PrimitiveParameters.h"
3 | #include "rv/RangeParameter.h"
4 | #include "rv/CompositeParameter.h"
5 |
6 | #include "rv/ParameterList.h"
7 |
8 | namespace rv
9 | {
10 |
11 | Parameter::operator ParameterList() const
12 | {
13 | std::stringstream str;
14 | str << "conversion of Parameter with name '" << mName
15 | << "' to ParameterList not possible.";
16 | throw Exception(str.str());
17 | }
18 |
19 | Parameter* Parameter::parseParameter(const XmlNode& node)
20 | {
21 | const std::string param_name = node.getAttribute("name");
22 | const std::string param_type = node.getAttribute("type");
23 |
24 | if (param_type == "integer")
25 | return parseIntegerParameter(node);
26 | else if (param_type == "float")
27 | return parseFloatParameter(node);
28 | else if (param_type == "string")
29 | return parseStringParameter(node);
30 | else if (param_type == "boolean")
31 | return parseBooleanParameter(node);
32 | // else if (param_type == "vector")
33 | // return parseVectorParameter(node);
34 | // else if (param_type == "matrix")
35 | // return parseMatrixParameter(node);
36 | else if (param_type == "range")
37 | return parseRangeParameter(node);
38 | else if (param_type == "composite")
39 | return parseCompositeParameter(node);
40 | else
41 | {
42 | std::stringstream reason;
43 | reason << "Unknown parameter type in xml file at line " << node.getLineNo()
44 | << std::endl;
45 | throw XmlError(reason.str());
46 | }
47 |
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/rv/ParameterListIterator.h:
--------------------------------------------------------------------------------
1 | #ifndef PARAMETERLISTITERATOR_H_
2 | #define PARAMETERLISTITERATOR_H_
3 |
4 | #include "ParameterList.h"
5 | #include "RangeParameter.h"
6 | #include
7 |
8 | namespace rv
9 | {
10 | /**
11 | * \brief iterates over a given ParameterList and replacing all RangeParameters with appropriate values.
12 | *
13 | * Generate instantiated ParameterList where all RangeParameters are replaced by appropriate values.
14 | * If no values are RangeParameters then only the ParameterList is returned, when next is called.
15 | *
16 | * \author: behley
17 | */
18 | class ParameterListIterator
19 | {
20 | public:
21 | ParameterListIterator(const ParameterList& params);
22 |
23 | /** \brief iterate only partially over some RangeParameters
24 | *
25 | * Only some of the RangeParameters are replaced, the rest are left in their original state. Thus,
26 | * it is possible to iterate in a nested fashion over the Parameters.
27 | */
28 | ParameterListIterator(const ParameterList& params,
29 | const std::vector& names);
30 |
31 | /** \brief new ParameterList with instantiated range params available? **/
32 | bool hasNext();
33 | /** \brief next ParameterList with instantiated Parameters, and advance to next params **/
34 | const ParameterList& next();
35 | /** \brief returns the RangeParameter, which are replaced. **/
36 | const std::vector& getRangeParams() const;
37 |
38 | protected:
39 | /** todo: implement copy constructors. **/
40 | ParameterListIterator(const ParameterListIterator& other);
41 | ParameterListIterator& operator=(const ParameterListIterator& other);
42 |
43 | /** \brief extract range parameters from ParameterList, and set parents. **/
44 | void parseParameterList(ParameterList& new_params,
45 | const ParameterList& ranged_params);
46 |
47 | bool mHasNext;
48 | /** idea for copy/assignment: use original parameter list, and iterator. **/
49 | ParameterList mOriginalParams;
50 | std::vector mFilter;
51 |
52 | /** fully instantiated ParameterList **/
53 | ParameterList mCurrentParams;
54 | /** original range parameters **/
55 | std::vector mRangeParams;
56 | /** extracted range parameters **/
57 | std::vector mRangeIters;
58 | /** pointers to ParameterLists where the i-th RangeParameter occurs. **/
59 | std::vector mRangeParamParents;
60 | };
61 |
62 | }
63 | #endif /* PARAMETERLISTITERATOR_H_ */
64 |
--------------------------------------------------------------------------------
/src/rv/Random.cpp:
--------------------------------------------------------------------------------
1 | #include "rv/Random.h"
2 |
3 | namespace rv
4 | {
5 |
6 | Random::Random() :
7 | uniform_(0.0, 1.0), normal_(0.0, 1.0)
8 | {
9 |
10 | }
11 |
12 | Random::Random(uint32_t seed) :
13 | rng_(seed), uniform_(0.0, 1.0), normal_(0.0, 1.0)
14 | {
15 |
16 | }
17 |
18 | int32_t Random::getInt(int32_t n)
19 | {
20 | return static_cast(n * uniform_(rng_));
21 | }
22 |
23 | int32_t Random::getInt(int32_t min, int32_t max)
24 | {
25 | return static_cast((max - min) * uniform_(rng_) + min);
26 | }
27 |
28 | int32_t Random::operator()(int32_t n)
29 | {
30 | return getInt(n);
31 | }
32 |
33 | float Random::getFloat()
34 | {
35 | return static_cast(uniform_(rng_));
36 | }
37 |
38 | float Random::getGaussianFloat()
39 | {
40 | return static_cast(uniform_(rng_));
41 | }
42 |
43 | double Random::getDouble()
44 | {
45 | return uniform_(rng_);
46 | }
47 |
48 | /** \brief Returns a random double from the Gaussian distribution with mean 0 and std. deviation 1 */
49 | double Random::getGaussianDouble()
50 | {
51 | return normal_(rng_);
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/rv/Random.h:
--------------------------------------------------------------------------------
1 | #ifndef CORE_RANDOM_H_
2 | #define CORE_RANDOM_H_
3 |
4 | #include
5 |
6 | namespace rv
7 | {
8 |
9 | /** \brief random number generator based on boost::random
10 | *
11 | * The class simply decorates the functionality in a more usable way.
12 | *
13 | *
14 | * \author snej
15 | */
16 |
17 | class Random
18 | {
19 | public:
20 | Random();
21 | explicit Random(uint32_t seed);
22 |
23 | /** \brief Returns a random integer value in the range [0,\a n - 1] inclusive. */
24 | int32_t getInt(int32_t n);
25 | /** \brief Returns a random integer value in the range [\a min,\a max] inclusive. */
26 | int32_t getInt(int32_t min, int32_t max);
27 |
28 | /** \brief Function operator for use with STL.
29 | *
30 | * Returns a random value between 0 and \a n - 1 inclusive.
31 | */
32 | int32_t operator()(int32_t n);
33 |
34 | /** \brief Returns a random float in the range [0,1] inclusive. */
35 | float getFloat();
36 | /** \brief Returns a random float from the Gaussian distribution with mean 0 and std. deviation 1 */
37 | float getGaussianFloat();
38 | /** \brief Returns a random double in the range [0,1] inclusive. */
39 | double getDouble();
40 | /** \brief Returns a random double from the Gaussian distribution with mean 0 and std. deviation 1 */
41 | double getGaussianDouble();
42 |
43 | protected:
44 | std::default_random_engine rng_;
45 | std::uniform_real_distribution uniform_;
46 | std::normal_distribution normal_;
47 | };
48 |
49 | }
50 |
51 | #endif /* CORE_RANDOM_H_ */
52 |
--------------------------------------------------------------------------------
/src/rv/RangeParameter.cpp:
--------------------------------------------------------------------------------
1 | #include "rv/RangeParameter.h"
2 | #include
3 | #include
4 |
5 | namespace rv
6 | {
7 |
8 | RangeParameter::RangeParameter(const std::string& name, const Parameter& begin,
9 | const Parameter& end, const Parameter& increment,
10 | const std::string& ) :
11 | CompositeParameter(name)
12 | {
13 | Parameter* param = begin.clone();
14 | param->name() = name;
15 | Parameter* endRenamed = end.clone();
16 | endRenamed->name() = name;
17 |
18 | for (; *param <= *endRenamed; *param += increment)
19 | values.push_back(param->clone());
20 |
21 | // values.push_back(endRenamed->clone()); // needed to ensure <=
22 |
23 | delete endRenamed;
24 | delete param;
25 |
26 | param_list.insert(begin);
27 | param_list.insert(end);
28 | param_list.insert(increment);
29 | }
30 |
31 | RangeParameter::RangeParameter(const std::string& name,
32 | const XmlNode& node) :
33 | CompositeParameter(name, node)
34 | {
35 | // determine if for or sequence
36 | if (param_list.hasParam("begin") && param_list.hasParam("end")
37 | && param_list.hasParam("increment"))
38 | {
39 | Parameter* param = param_list["begin"].clone();
40 | param->name() = name;
41 | Parameter* endRenamed = param_list["end"].clone();
42 | endRenamed->name() = name;
43 |
44 | for (; *param <= *endRenamed; *param += param_list["increment"])
45 | values.push_back(param->clone());
46 |
47 | // values.push_back(endRenamed->clone());
48 | delete endRenamed;
49 | delete param;
50 | }
51 | else
52 | {
53 | for (ParameterList::const_iterator it = param_list.begin(); it
54 | != param_list.end(); ++it)
55 | {
56 | Parameter* param = it->clone();
57 | // rename this.
58 | param->name() = name;
59 | values.push_back(param);
60 | }
61 | }
62 |
63 | if(values.begin() == values.end())
64 | throw Exception("RangeParameter initialization failed! No child params found.");
65 |
66 | }
67 |
68 | RangeParameter::RangeParameter(const std::string& name, const ParameterList& vals)
69 | : CompositeParameter(name, vals)
70 | {
71 | for (ParameterList::const_iterator it = param_list.begin(); it
72 | != param_list.end(); ++it)
73 | {
74 | Parameter* param = it->clone();
75 | // rename this.
76 | param->name() = name;
77 | values.push_back(param);
78 | }
79 | }
80 |
81 | RangeParameter::~RangeParameter()
82 | {
83 | for (std::list::const_iterator it = values.begin(); it
84 | != values.end(); ++it)
85 | delete *it;
86 | values.clear();
87 | }
88 |
89 | RangeParameter::RangeParameter(const RangeParameter& other)
90 | : CompositeParameter(other)
91 | {
92 | for (std::list::const_iterator it = other.values.begin(); it
93 | != other.values.end(); ++it)
94 | values.push_back((*it)->clone());
95 | }
96 |
97 | RangeParameter& RangeParameter::operator=(const RangeParameter& other)
98 | {
99 | if (&other == this) return *this;
100 |
101 | for (std::list::const_iterator it = values.begin(); it
102 | != values.end(); ++it)
103 | delete *it;
104 | values.clear();
105 |
106 | for (std::list::const_iterator it = other.values.begin(); it
107 | != other.values.end(); ++it)
108 | values.push_back((*it)->clone());
109 |
110 | return *this;
111 | }
112 |
113 | RangeParameter* RangeParameter::clone() const
114 | {
115 | return new RangeParameter(*this);
116 | }
117 |
118 | std::string RangeParameter::toString() const
119 | {
120 | std::stringstream out;
121 |
122 | out << "" << std::endl;
123 | for (ParameterList::const_iterator it = param_list.begin(); it
124 | != param_list.end(); ++it)
125 | out << " " << *it << std::endl;
126 | out << "";
127 |
128 | return out.str();
129 | }
130 |
131 | RangeParameter* parseRangeParameter(const XmlNode& node)
132 | {
133 | if (node.getAttribute("type") == "range")
134 | return new RangeParameter(node.getAttribute("name"), node);
135 | else
136 | throw XmlError("Error while parameter parsing: 'type' not 'string'");
137 | }
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/src/rv/RingBuffer.h:
--------------------------------------------------------------------------------
1 | /** \brief ring buffer with predefined capacity
2 | *
3 | * a ring buffer offers push_back and push_front mechanism
4 | * to add elements with a predefined capacity, i.e.,the
5 | * buffer size will never be larger than capacity.
6 | * Thus elements at the end or the beginning are dropped,
7 | * if the capacity is reached. The order of elements is
8 | * preserved.
9 | *
10 | *
11 | * \author behley
12 | */
13 |
14 | #ifndef RINGBUFFER_H_
15 | #define RINGBUFFER_H_
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 |
22 | namespace rv
23 | {
24 |
25 | template
26 | class RingBuffer
27 | {
28 | public:
29 | /** \brief initialize a buffer of capacity c **/
30 | RingBuffer(uint32_t c) :
31 | mStart(0), mCapacity(c), mSize(0), mData(c)
32 | {
33 | }
34 |
35 | /** \brief access the i-th element
36 | * \param index of element in the buffer.
37 | **/
38 | T& operator[](uint32_t i);
39 | const T& operator[](uint32_t i) const;
40 |
41 | /** \brief insert an element at the end, but this might drop an element at the front. **/
42 | void push_back(const T& value);
43 |
44 | /** \brief insert an element at the front, but this might drop an element at the end. **/
45 | void push_front(const T& value);
46 |
47 | /** \brief number of items in buffer **/
48 | uint32_t size() const
49 | {
50 | return mSize;
51 | }
52 |
53 | /** \brief capacity. **/
54 | uint32_t capacity() const
55 | {
56 | return mCapacity;
57 | }
58 |
59 | /** \brief remove all elements from the buffer. **/
60 | void clear()
61 | {
62 | mStart = 0;
63 | mSize = 0;
64 | }
65 |
66 | friend std::ostream& operator<<(std::ostream& out, const RingBuffer& buffer)
67 | {
68 | out << "[";
69 | if (buffer.mSize > 0) out << buffer.mData[buffer.mStart];
70 | for (uint32_t i = 1; i < buffer.mSize; ++i)
71 | {
72 | out << ", " << buffer.mData[(i + buffer.mStart) % buffer.mCapacity];
73 | }
74 | out << "]";
75 |
76 | return out;
77 | }
78 |
79 | private:
80 | uint32_t mStart;
81 | uint32_t mCapacity;
82 | uint32_t mSize;
83 | std::vector mData;
84 | };
85 |
86 | template
87 | T& RingBuffer::operator[](uint32_t i)
88 | {
89 | assert(i < mSize);
90 | return mData[(i + mStart) % mCapacity];
91 | }
92 |
93 | template
94 | const T& RingBuffer::operator[](uint32_t i) const
95 | {
96 | assert(i < mSize);
97 | return mData[(i + mStart) % mCapacity];
98 | }
99 |
100 | template
101 | void RingBuffer::push_back(const T& value)
102 | {
103 | mData[(mStart + mSize) % mCapacity] = value;
104 | if (mSize == mCapacity) mStart = (mStart + 1) % mCapacity;
105 | if (mSize < mCapacity) ++mSize;
106 | }
107 |
108 | template
109 | void RingBuffer::push_front(const T& value)
110 | {
111 | mStart = (mStart - 1 + mCapacity) % mCapacity;
112 | mData[mStart] = value;
113 | if (mSize < mCapacity) ++mSize;
114 | }
115 |
116 | }
117 | #endif /* RINGBUFFER_H_ */
118 |
--------------------------------------------------------------------------------
/src/rv/Stopwatch.cpp:
--------------------------------------------------------------------------------
1 | #include "Stopwatch.h"
2 | #include
3 |
4 | namespace rv
5 | {
6 |
7 | std::vector Stopwatch::stimes =
8 | std::vector();
9 |
10 | void Stopwatch::tic()
11 | {
12 | stimes.push_back(std::chrono::high_resolution_clock::now());
13 | }
14 |
15 | /** \brief stops the last timer started and outputs \a msg, if given.
16 | *
17 | * \return elapsed time in seconds.
18 | **/
19 | double Stopwatch::toc()
20 | {
21 | assert(stimes.begin() != stimes.end());
22 |
23 | std::chrono::system_clock::time_point endtime = std::chrono::high_resolution_clock::now();
24 | std::chrono::system_clock::time_point starttime = stimes.back();
25 | stimes.pop_back();
26 |
27 | std::chrono::duration elapsed_seconds = endtime - starttime;
28 |
29 | return elapsed_seconds.count();
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/rv/Stopwatch.h:
--------------------------------------------------------------------------------
1 | /**
2 | * \headerfile Stopwatch.h
3 | *
4 | * \brief simple timing of function calls.
5 | *
6 | * Mimics the behavior of tic/toc of Matlab and throws an error if toc() is called without starting a timer before.
7 | *
8 | * Example:
9 | * Stopwatch::tic(); starts timer A
10 | *
11 | * Stopwatch::tic(); starts timer B
12 | * method1();
13 | * double time1 = Stopwatch::toc(); stops timer B and returns time elapsed since timer B started
14 | *
15 | * Stopwatch::tic(); starts timer C
16 | * method2();
17 | * double time2 = Stopwatch::toc(); stops timer C
18 | *
19 | * Stopwatch::toc("finished"); stops timer A, thus this is approx. time1 + time2. and outputs a message.
20 | *
21 | * \author behley
22 | */
23 |
24 | #ifndef STOPWATCH_H_
25 | #define STOPWATCH_H_
26 |
27 | #include
28 | #include
29 | #include
30 |
31 | namespace rv {
32 |
33 | class Stopwatch {
34 | public:
35 | /** \brief starts a timer. **/
36 | static void tic();
37 | /** \brief stops the last timer started and outputs \a msg **/
38 | static double toc();
39 | /** \brief number of active stopwatches. **/
40 | static size_t active() { return stimes.size(); }
41 |
42 | protected:
43 | static std::vector stimes;
44 | };
45 | }
46 |
47 | #endif /* STOPWATCH_H_ */
48 |
--------------------------------------------------------------------------------
/src/rv/XmlDocument.cpp:
--------------------------------------------------------------------------------
1 | #include "rv/XmlDocument.h"
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include "yxml.h"
8 | #include "rv/XmlError.h"
9 |
10 | namespace rv
11 | {
12 |
13 | XmlDocument::XmlDocument()
14 | {
15 |
16 | }
17 |
18 | const XmlNode& XmlDocument::root() const
19 | {
20 | return root_;
21 | }
22 |
23 | XmlDocument XmlDocument::fromFile(const std::string& filename, bool partial)
24 | {
25 | std::ifstream in(filename.c_str());
26 | XmlDocument doc = fromStream(in, partial);
27 | in.close();
28 |
29 | return doc;
30 | }
31 |
32 | XmlDocument XmlDocument::fromData(const std::string& buffer, bool partial)
33 | {
34 | std::istringstream in(buffer);
35 |
36 | return fromStream(in, partial);
37 | }
38 |
39 | XmlDocument XmlDocument::fromStream(std::istream& stream, bool partial)
40 | {
41 | void* buf = malloc(4096);
42 | yxml_t x;
43 | yxml_init(&x, buf, 4096);
44 |
45 | XmlDocument doc;
46 | std::vector currentNodes; // stack of nodes that are currently initialized.
47 |
48 | yxml_ret_t ret;
49 |
50 | XmlNode* node = 0;
51 | std::string attr_value;
52 | std::string content;
53 |
54 | stream.peek();
55 | while (!stream.eof())
56 | {
57 | ret = yxml_parse(&x, stream.get());
58 |
59 | switch (ret)
60 | {
61 | case YXML_ELEMSTART:
62 | node = 0;
63 | if (currentNodes.empty())
64 | {
65 | node = &doc.root_;
66 | }
67 | else
68 | {
69 | // Note: this could alter the pointers in currentNodes;
70 | // However, the invariant is that parent.children are already initialized!
71 | XmlNode* parent = currentNodes.back();
72 | parent->children_.push_back(XmlNode());
73 | node = &parent->children_.back();
74 | }
75 |
76 | assert(node != 0);
77 | node->valid_ = partial;
78 | node->lineNumber_ = x.line;
79 | node->tag_name_ = x.elem;
80 |
81 | currentNodes.push_back(node);
82 | content.clear();
83 | break;
84 |
85 | case YXML_CONTENT:
86 | content += x.data;
87 | break;
88 |
89 | case YXML_ELEMEND:
90 | node = currentNodes.back();
91 |
92 | if (node->children_.size() == 0) node->content_ = content;
93 |
94 | node->valid_ = true;
95 | currentNodes.pop_back();
96 |
97 | break;
98 |
99 | case YXML_ATTRSTART:
100 | attr_value.clear();
101 | break;
102 |
103 | case YXML_ATTRVAL:
104 | attr_value += x.data;
105 | break;
106 |
107 | case YXML_ATTREND:
108 | node = currentNodes.back();
109 | node->attributes_[x.attr] = attr_value;
110 | break;
111 |
112 | case YXML_OK:
113 | case YXML_PISTART:
114 | case YXML_PICONTENT:
115 | case YXML_PIEND:
116 | // unhandled.
117 | break;
118 |
119 | case YXML_EEOF:
120 | case YXML_EREF:
121 | case YXML_ECLOSE:
122 | case YXML_ESTACK:
123 | case YXML_ESYN:
124 | free(buf);
125 |
126 | std::stringstream reason;
127 | reason << "Error while parsing in line " << x.line;
128 | throw XmlError(reason.str());
129 | break;
130 | }
131 |
132 | stream.peek();
133 | }
134 |
135 | free(buf);
136 |
137 | if (!partial && (currentNodes.size() > 0)) throw XmlError("Error while parsing: Unexpected EOF.");
138 |
139 | return doc;
140 | }
141 |
142 | } /* namespace rv */
143 |
--------------------------------------------------------------------------------
/src/rv/XmlDocument.h:
--------------------------------------------------------------------------------
1 | #ifndef CORE_XMLDOCUMENT_H_
2 | #define CORE_XMLDOCUMENT_H_
3 |
4 | #include "XmlNode.h"
5 | #include "XmlError.h"
6 |
7 | /** \brief
8 | *
9 | * \author behley
10 | */
11 |
12 | namespace rv
13 | {
14 |
15 | class XmlDocument
16 | {
17 | public:
18 | /** \brief Returns the root node of the XML document. */
19 | const XmlNode& root() const;
20 |
21 | /** \brief Reads an XML document from a file.
22 | *
23 | * \param[in] fileName the file to be read
24 | * \param[in] partial do we generate document for partial/incomplete XML files? (default: false)
25 | * \throw XmlError if the file could not be read or is malformed
26 | */
27 | static XmlDocument fromFile(const std::string& fileName, bool partial = false);
28 |
29 | /** \brief Reads an XML document from a string.
30 | * \param[in] buffer
31 | * \param[in] partial do we generate document for partial/incomplete XML files? (default: false)
32 | * \throw XmlError if the document is malformed
33 | */
34 | static XmlDocument fromData(const std::string& buffer, bool partial = false);
35 |
36 | /** \brief Reads an XML document from a input stream.
37 | *
38 | * \param in input stream
39 | * \param partial do we generate document for partial/incomplete XML files? (default: false)
40 | * \return xml document
41 | * \throw XmlError if the document is malformed
42 | */
43 | static XmlDocument fromStream(std::istream& in, bool partial = false);
44 |
45 | protected:
46 | XmlDocument();
47 |
48 | XmlNode root_;
49 | };
50 |
51 | } /* namespace rv */
52 | #endif /* SRC_CORE_RV_XMLDOCUMENT_H_ */
53 |
--------------------------------------------------------------------------------
/src/rv/XmlError.h:
--------------------------------------------------------------------------------
1 | #ifndef CORE_XMLERROR_H_
2 | #define CORE_XMLERROR_H_
3 |
4 | #include
5 |
6 | /** \brief Exception thrown in case of an error while parsing XML files.
7 | *
8 | * \author behley
9 | */
10 |
11 | namespace rv
12 | {
13 |
14 | class XmlError: public std::runtime_error
15 | {
16 | public:
17 | XmlError(const std::string& reason) :
18 | std::runtime_error(reason)
19 | {
20 | }
21 | };
22 |
23 | }
24 | #endif /* SRC_CORE_RV_XMLERROR_H_ */
25 |
--------------------------------------------------------------------------------
/src/rv/XmlNode.cpp:
--------------------------------------------------------------------------------
1 | #include "rv/XmlNode.h"
2 | #include "rv/XmlError.h"
3 |
4 | namespace rv
5 | {
6 |
7 | XmlNode::XmlNode() :
8 | valid_(false), lineNumber_(0)
9 | {
10 |
11 | }
12 |
13 | //XmlNode::~XmlNode()
14 | //{
15 | // for (uint32_t i = 0; i < children_.size(); ++i)
16 | // delete children_[i];
17 | //}
18 |
19 | //XmlNode::XmlNode(const XmlNode& other) :
20 | // valid_(other.valid_), tag_name_(other.tag_name_), content_(other.content_), attributes_(other.attributes_), lineNumber_(
21 | // other.lineNumber_)
22 | //{
23 | // for (uint32_t i = 0; i < other.children_.size(); ++i)
24 | // children_.push_back(new XmlNode(*other.children_[i]));
25 | //}
26 |
27 | //XmlNode& XmlNode::operator=(const XmlNode& other)
28 | //{
29 | // valid_ = other.valid_;
30 | // tag_name_ = other.tag_name_;
31 | // content_ = other.content_;
32 | // attributes_ = other.attributes_;
33 | // lineNumber_ = other.lineNumber_;
34 | //
35 | // for (uint32_t i = 0; i < children_.size(); ++i)
36 | // delete children_[i];
37 | // children_.clear();
38 | //
39 | // for (uint32_t i = 0; i < other.children_.size(); ++i)
40 | // children_.push_back(new XmlNode(*other.children_[i]));
41 | //
42 | // return *this;
43 | //}
44 |
45 | bool XmlNode::isValid() const
46 | {
47 | return valid_;
48 | }
49 |
50 | /** \brief Returns the tag name. */
51 | const std::string& XmlNode::getName() const
52 | {
53 | return tag_name_;
54 | }
55 |
56 | /** \brief Returns the node content, i.e. the data between opening and closing tag */
57 | const std::string& XmlNode::getContent() const
58 | {
59 | return content_;
60 | }
61 |
62 | uint32_t XmlNode::getLineNo() const
63 | {
64 |
65 | return lineNumber_;
66 | }
67 |
68 | /** \brief Returns true if the node has at least one child node with the given tag name. */
69 | bool XmlNode::hasChild(const std::string& name) const
70 | {
71 | for (List::const_iterator it = children_.begin(); it != children_.end(); ++it)
72 | if (it->tag_name_ == name) return true;
73 |
74 | return false;
75 | }
76 |
77 | /** \brief Returns true if the node has the given attribute. */
78 | bool XmlNode::hasAttribute(const std::string& name) const
79 | {
80 | return (attributes_.find(name) != attributes_.end());
81 | }
82 |
83 | /** \brief Returns the value of the given attribute.
84 | * \throw XmlError if the node has no attribute \a name */
85 | std::string XmlNode::getAttribute(const std::string& name) const
86 | {
87 | if (attributes_.find(name) == attributes_.end()) throw XmlError("Attribute not found");
88 |
89 | return attributes_.find(name)->second;
90 | }
91 |
92 | /** \brief Returns a list of all child nodes. */
93 | const XmlNode::List& XmlNode::getChildren() const
94 | {
95 | return children_;
96 | }
97 |
98 | XmlNode::List XmlNode::getChildren(const std::string& name) const
99 | {
100 | XmlNode::List matches;
101 | for (List::const_iterator it = children_.begin(); it != children_.end(); ++it)
102 | if (it->tag_name_ == name) matches.push_back(*it);
103 |
104 | return matches;
105 | }
106 |
107 | XmlNode XmlNode::getChild(const std::string& name) const
108 | {
109 | for (List::const_iterator it = children_.begin(); it != children_.end(); ++it)
110 | {
111 | if (it->tag_name_ == name) return *it;
112 | }
113 |
114 | return XmlNode();
115 | }
116 |
117 | XmlNode::List XmlNode::getChildrenWith(const std::string& name, const std::string& value) const
118 | {
119 | XmlNode::List matches;
120 | for (List::const_iterator it = children_.begin(); it != children_.end(); ++it)
121 | {
122 | if (!it->hasAttribute(name)) continue;
123 | if (it->attributes_.find(name)->second == value) matches.push_back(*it);
124 | }
125 |
126 | return matches;
127 | }
128 |
129 | XmlNode XmlNode::getChildWith(const std::string& name, const std::string& value) const
130 | {
131 | for (List::const_iterator it = children_.begin(); it != children_.end(); ++it)
132 | {
133 | if (!it->hasAttribute(name)) continue;
134 | if (it->attributes_.find(name)->second == value) return *it;
135 | }
136 |
137 | return XmlNode();
138 | }
139 |
140 | } /* namespace rv */
141 |
142 |
--------------------------------------------------------------------------------
/src/rv/XmlNode.h:
--------------------------------------------------------------------------------
1 | #ifndef CORE_XMLNODE_H_
2 | #define CORE_XMLNODE_H_
3 |
4 | #include
5 | #include
6 | #include