├── lsd_slam_to_pcl_nodelet.xml
├── include
└── lsd_slam_to_pcl
│ ├── lsd_slam_to_pcl_nodelet.hpp
│ ├── lsd_slam_to_pcl.hpp
│ └── third_party
│ └── sophus
│ ├── sophus.hpp
│ ├── rxso3.hpp
│ ├── so3.hpp
│ └── sim3.hpp
├── msg
└── keyframeMsg.msg
├── cfg
└── Params.cfg
├── src
├── lsd_slam_to_pcl_nodelet.cpp
└── lsd_slam_to_pcl.cpp
├── README.md
├── package.xml
└── CMakeLists.txt
/lsd_slam_to_pcl_nodelet.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | A node to convert lsd_slam depth keyframes to pcl PointClouds
5 |
6 |
7 |
--------------------------------------------------------------------------------
/include/lsd_slam_to_pcl/lsd_slam_to_pcl_nodelet.hpp:
--------------------------------------------------------------------------------
1 | #ifndef LSD_SLAM_TO_PCL_NODELET_HPP
2 | #define LSD_SLAM_TO_PCL_NODELET_HPP
3 |
4 | #include
5 | #include
6 |
7 | #include "lsd_slam_to_pcl/lsd_slam_to_pcl.hpp"
8 |
9 | class LSDSLAMToPCLNodelet : public nodelet::Nodelet
10 | {
11 | public:
12 | LSDSLAMToPCLNodelet();
13 | ~LSDSLAMToPCLNodelet();
14 |
15 | virtual void onInit();
16 |
17 | private:
18 | boost::shared_ptr controller_;
19 | boost::shared_ptr nh_ptr_;
20 |
21 | };
22 |
23 | #endif
--------------------------------------------------------------------------------
/msg/keyframeMsg.msg:
--------------------------------------------------------------------------------
1 | int32 id
2 | float64 time
3 | bool isKeyframe
4 |
5 | # camToWorld as serialization of sophus sim(3).
6 | # may change with keyframeGraph-updates.
7 | float32[7] camToWorld
8 |
9 |
10 | # camera parameter (fx fy cx cy), width, height
11 | # will never change, but required for display.
12 | float32 fx
13 | float32 fy
14 | float32 cx
15 | float32 cy
16 | uint32 height
17 | uint32 width
18 |
19 |
20 | # data as InputPointDense (float idepth, float idepth_var, uchar color[4]), width x height
21 | # may be empty, in that case no associated pointcloud is ever shown.
22 | uint8[] pointcloud
23 |
--------------------------------------------------------------------------------
/cfg/Params.cfg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | PACKAGE = "lsd_slam_to_pcl"
3 |
4 | from dynamic_reconfigure.parameter_generator_catkin import *
5 |
6 | gen = ParameterGenerator()
7 |
8 | gen.add("min_near_support", int_t, 0, "only plot points that have #min_near_support similar neighbours (higher values remove outliers)", 7, 0, 9)
9 | gen.add("sparsify_factor", int_t, 0, "only plot one out of #sparsify_factor points, selected at random. Use this to significantly speed up rendering for large maps.", 1, 1, 100)
10 | gen.add("depth_var_thresh_abs", double_t, 0, "log10 of threshold on point's variance, in absolute scale.", -1, -10, 1)
11 | gen.add("depth_var_thresh_scaled", double_t, 0, "log10 of threshold on point's variance, in the respective keyframe's scale. ", -3, -10, 1)
12 |
13 | exit(gen.generate(PACKAGE, "lsd_slam_to_pcl", "Params"))
--------------------------------------------------------------------------------
/src/lsd_slam_to_pcl_nodelet.cpp:
--------------------------------------------------------------------------------
1 | #include "lsd_slam_to_pcl/lsd_slam_to_pcl_nodelet.hpp"
2 |
3 | LSDSLAMToPCLNodelet::LSDSLAMToPCLNodelet() {}
4 | LSDSLAMToPCLNodelet::~LSDSLAMToPCLNodelet() {}
5 |
6 | void LSDSLAMToPCLNodelet::onInit()
7 | {
8 | nh_ptr_ = boost::make_shared(this->getPrivateNodeHandle());
9 |
10 | std::string name = nh_ptr_->getUnresolvedNamespace();
11 | int pos = name.find_last_of('/');
12 | name = name.substr(pos + 1);
13 |
14 | NODELET_INFO_STREAM("Initialising nodelet... [" << name << "]");
15 | controller_.reset(new LSDSLAMToPCL(*nh_ptr_, name));
16 |
17 | if (controller_->Init())
18 | {
19 | NODELET_INFO_STREAM("Nodelet initialised. [" << name << "]");
20 | }
21 | else
22 | {
23 | NODELET_ERROR_STREAM("Couldn't initialise nodelet! Please restart. [" << name << "]");
24 | }
25 | }
26 |
27 | PLUGINLIB_EXPORT_CLASS(LSDSLAMToPCLNodelet, nodelet::Nodelet);
28 |
--------------------------------------------------------------------------------
/include/lsd_slam_to_pcl/lsd_slam_to_pcl.hpp:
--------------------------------------------------------------------------------
1 | #ifndef LSD_SLAM_TO_PCL_HPP
2 | #define LSD_SLAM_TO_PCL_HPP
3 |
4 | #include
5 | #include
6 | #include "third_party/sophus/sim3.hpp"
7 |
8 | #include "lsd_slam_to_pcl/keyframeMsg.h"
9 |
10 | struct InputPointDense
11 | {
12 | float idepth;
13 | float idepth_var;
14 | unsigned char color[4];
15 | };
16 |
17 | class LSDSLAMToPCL
18 | {
19 | public:
20 | LSDSLAMToPCL(ros::NodeHandle& nh, std::string& name);
21 | ~LSDSLAMToPCL();
22 |
23 | bool Init();
24 |
25 | private:
26 | ros::NodeHandle nh_;
27 | ros::Subscriber depth_subscriber_;
28 | ros::Publisher cloud_publisher_;
29 | ros::Publisher indices_publisher_;
30 |
31 | pcl::PointXYZRGB point_invalid_;
32 | std::string node_name_;
33 | int sparsify_factor_;
34 | int min_near_support_;
35 | float scaled_depth_var_thresh_;
36 | float abs_depth_var_thresh_;
37 |
38 | void depthCB(const lsd_slam_to_pcl::keyframeMsgConstPtr msg);
39 | };
40 |
41 | #endif
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LSD-SLAM to PCL
2 |
3 | This package is a nodelet that converts [LSD_SLAM](https://github.com/tum-vision/lsd_slam) keyframes into PCL pointclouds (`pcl::PointCloud`).
4 |
5 | To build:
6 |
7 | ```bash
8 | cd ~/catkin_ws/src
9 | git clone https://github.com/alexanderkoumis/lsd_slam_to_pcl
10 | cd ..
11 | catkin_make
12 | ```
13 |
14 | Using it in a launch file:
15 | ```xml
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | ```
28 |
29 | Topics `lsd_slam_to_pcl/output_points` and `lsd_slam_to_pcl/output_indices` can now be subscribed to as `pcl::PointCloud::ConstPtr` and `pcl::PointIndicesConstPtr`, respectively.
30 |
31 |
--------------------------------------------------------------------------------
/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | lsd_slam_to_pcl
4 | 0.0.0
5 | The lsd_slam_to_pcl package
6 |
7 | Alexander Koumis
8 | TODO
9 |
10 | catkin
11 |
12 | message_generation
13 | message_runtime
14 | pcl_ros
15 | pluginlib
16 | roscpp
17 | std_msgs
18 | nodelet
19 |
20 | message_generation
21 | message_runtime
22 | pcl_ros
23 | pluginlib
24 | roscpp
25 | std_msgs
26 | nodelet
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.3)
2 | project(lsd_slam_to_pcl)
3 |
4 | set(CMAKE_BUILD_TYPE Release)
5 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -ggdb -gdwarf-3")
6 |
7 | find_package(catkin REQUIRED COMPONENTS
8 | message_generation
9 | message_runtime
10 | pcl_ros
11 | pluginlib
12 | roscpp
13 | std_msgs
14 | )
15 |
16 | add_message_files(
17 | FILES
18 | keyframeMsg.msg
19 | )
20 |
21 | generate_messages(
22 | DEPENDENCIES
23 | std_msgs
24 | sensor_msgs
25 | )
26 |
27 | catkin_package(
28 | INCLUDE_DIRS include
29 | LIBRARIES lsd_slam_to_pcl_nodelet
30 | CATKIN_DEPENDS message_generation message_runtime pcl_ros pluginlib roscpp std_msgs
31 | )
32 |
33 | include_directories(
34 | ${catkin_INCLUDE_DIRS}
35 | include
36 | )
37 |
38 | add_library(lsd_slam_to_pcl_nodelet
39 | src/lsd_slam_to_pcl.cpp
40 | src/lsd_slam_to_pcl_nodelet.cpp
41 | )
42 |
43 | add_dependencies(lsd_slam_to_pcl_nodelet lsd_slam_to_pcl_gencpp)
44 |
45 | target_link_libraries(lsd_slam_to_pcl_nodelet
46 | ${catkin_LIBRARIES}
47 | )
48 |
49 | install(TARGETS lsd_slam_to_pcl_nodelet
50 | DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
51 | )
52 |
53 | install(DIRECTORY plugins
54 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
55 | )
56 |
57 | install(DIRECTORY launch
58 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
59 | )
--------------------------------------------------------------------------------
/include/lsd_slam_to_pcl/third_party/sophus/sophus.hpp:
--------------------------------------------------------------------------------
1 | // This file is part of Sophus.
2 | //
3 | // Copyright 2013 Hauke Strasdat
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to
7 | // deal in the Software without restriction, including without limitation the
8 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 | // sell copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 | // IN THE SOFTWARE.
22 |
23 | #ifndef SOPHUS_HPP
24 | #define SOPHUS_HPP
25 |
26 | #include
27 |
28 | #include
29 | #include
30 |
31 | namespace Sophus {
32 | using namespace Eigen;
33 |
34 | template
35 | struct SophusConstants {
36 | EIGEN_ALWAYS_INLINE static
37 | const Scalar epsilon() {
38 | return static_cast(1e-10);
39 | }
40 |
41 | EIGEN_ALWAYS_INLINE static
42 | const Scalar pi() {
43 | return static_cast(M_PI);
44 | }
45 | };
46 |
47 | template<>
48 | struct SophusConstants {
49 | EIGEN_ALWAYS_INLINE static
50 | const float epsilon() {
51 | return static_cast(1e-5);
52 | }
53 |
54 | EIGEN_ALWAYS_INLINE static
55 | const float pi() {
56 | return static_cast(M_PI);
57 | }
58 | };
59 |
60 | class SophusException : public std::runtime_error {
61 | public:
62 | SophusException (const std::string& str)
63 | : runtime_error("Sophus exception: " + str) {
64 | }
65 | };
66 |
67 | }
68 |
69 | #endif
70 |
--------------------------------------------------------------------------------
/src/lsd_slam_to_pcl.cpp:
--------------------------------------------------------------------------------
1 | #include "lsd_slam_to_pcl/lsd_slam_to_pcl.hpp"
2 |
3 | #include // quiet_NaN
4 |
5 | #include
6 | #include
7 | #include
8 |
9 |
10 | LSDSLAMToPCL::LSDSLAMToPCL(ros::NodeHandle& nh, std::string& name) :
11 | nh_(nh),
12 | node_name_(name),
13 | sparsify_factor_(0),
14 | min_near_support_(5),
15 | scaled_depth_var_thresh_(1),
16 | abs_depth_var_thresh_(1)
17 | {}
18 |
19 | LSDSLAMToPCL::~LSDSLAMToPCL() {}
20 |
21 | bool LSDSLAMToPCL::Init()
22 | {
23 | depth_subscriber_ = nh_.subscribe("input", 10, &LSDSLAMToPCL::depthCB, this);
24 | cloud_publisher_ = nh_.advertise >("output_points", 10);
25 | indices_publisher_ = nh_.advertise("output_indices", 10);
26 |
27 | point_invalid_.x = std::numeric_limits::quiet_NaN();
28 | point_invalid_.y = std::numeric_limits::quiet_NaN();
29 | point_invalid_.z = std::numeric_limits::quiet_NaN();
30 | point_invalid_.data[3] = 1.0f;
31 | return true;
32 | }
33 |
34 | void LSDSLAMToPCL::depthCB(const lsd_slam_to_pcl::keyframeMsgConstPtr msg)
35 | {
36 | if (msg->isKeyframe)
37 | {
38 |
39 | pcl::PointCloud cloud_pcl;
40 | pcl_ros::PCLNodelet::PointIndices indices_ros;
41 |
42 | const float fxi = 1 / msg->fx;
43 | const float fyi = 1 / msg->fy;
44 | const float cxi = -msg->cx / msg->fx;
45 | const float cyi = -msg->cy / msg->fy;
46 |
47 | const int width = msg->width;
48 | const int height = msg->height;
49 |
50 | int num_pts_total = 0;
51 |
52 | boost::shared_ptr input_points(new InputPointDense[width * height]);
53 |
54 | Sophus::Sim3f cam_to_world = Sophus::Sim3f();
55 |
56 | memcpy(cam_to_world.data(), msg->camToWorld.data(), 7 * sizeof(float));
57 | memcpy(input_points.get(), msg->pointcloud.data(), width * height * sizeof(InputPointDense));
58 |
59 | const float cam_to_world_scale = cam_to_world.scale();
60 |
61 | for (int y = 0; y < height; ++y)
62 | {
63 | for (int x = 0; x < width; ++x)
64 | {
65 |
66 | // Catch boundary points
67 | if (x == 0 || y == 0 || x == width - 1 || y == height - 1)
68 | {
69 | cloud_pcl.push_back(point_invalid_);
70 | continue;
71 | }
72 |
73 | int point_indice = x + y * width;
74 |
75 | const InputPointDense point_in_curr = input_points.get()[point_indice];
76 |
77 | if (point_in_curr.idepth <= 0)
78 | {
79 | cloud_pcl.push_back(point_invalid_);
80 | continue;
81 | }
82 |
83 | num_pts_total++;
84 |
85 | if (sparsify_factor_ > 1 && rand() % sparsify_factor_ != 0)
86 | {
87 | cloud_pcl.push_back(point_invalid_);
88 | continue;
89 | }
90 |
91 | const float depth = 1 / point_in_curr.idepth;
92 | const float depth4 = depth * depth * depth * depth;
93 |
94 | if (point_in_curr.idepth_var * depth4 > scaled_depth_var_thresh_)
95 | {
96 | cloud_pcl.push_back(point_invalid_);
97 | continue;
98 | }
99 |
100 | if (point_in_curr.idepth_var * depth4 * cam_to_world_scale * cam_to_world_scale > abs_depth_var_thresh_)
101 | {
102 | cloud_pcl.push_back(point_invalid_);
103 | continue;
104 | }
105 |
106 | if (min_near_support_ > 1)
107 | {
108 | int near_support = 0;
109 |
110 | for (int dx = -1; dx < 2; ++dx)
111 | {
112 | for (int dy = -1; dy < 2; ++dy)
113 | {
114 |
115 | const InputPointDense point_in_near = input_points.get()[x + dx + (y + dy) * width];
116 |
117 | if (point_in_near.idepth > 0)
118 | {
119 | const float diff = point_in_near.idepth - point_in_curr.idepth;
120 | if (diff * diff < 2 * point_in_curr.idepth_var) { near_support++; }
121 | }
122 | }
123 | }
124 |
125 | if (near_support < min_near_support_)
126 | {
127 | cloud_pcl.push_back(point_invalid_);
128 | continue;
129 | }
130 | }
131 |
132 | Eigen::Vector3f point_eigen = cam_to_world * (Eigen::Vector3f((x * fxi + cxi), (y * fyi + cyi), 1) * depth);
133 |
134 | pcl::PointXYZRGB point_pcl;
135 | point_pcl.x = point_eigen[0];
136 | point_pcl.y = point_eigen[1];
137 | point_pcl.z = point_eigen[2];
138 | point_pcl.r = point_in_curr.color[2];
139 | point_pcl.g = point_in_curr.color[1];
140 | point_pcl.b = point_in_curr.color[0];
141 | cloud_pcl.push_back(point_pcl);
142 | indices_ros.indices.push_back(point_indice);
143 |
144 | }
145 | }
146 |
147 | cloud_pcl.width = width;
148 | cloud_pcl.height = height;
149 | cloud_pcl.is_dense = false;
150 | cloud_pcl.header.frame_id = "frame";
151 | pcl_conversions::toPCL(ros::Time::now(), cloud_pcl.header.stamp);
152 | indices_ros.header.stamp = ros::Time::now();
153 |
154 | cloud_publisher_.publish(boost::make_shared >(cloud_pcl));
155 | indices_publisher_.publish(boost::make_shared(indices_ros));
156 |
157 | ROS_DEBUG_STREAM("Published " << num_pts_total << " points to pointcloud, dimensions [" << cloud_pcl.width << " " << cloud_pcl.height << "]");
158 |
159 | }
160 |
161 | else
162 | {
163 | ROS_DEBUG_STREAM("Error, must subscribe to keyframe");
164 | }
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/include/lsd_slam_to_pcl/third_party/sophus/rxso3.hpp:
--------------------------------------------------------------------------------
1 | // This file is part of Sophus.
2 | //
3 | // Copyright 2012-2013 Hauke Strasdat
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to
7 | // deal in the Software without restriction, including without limitation the
8 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 | // sell copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 | // IN THE SOFTWARE.
22 |
23 | #ifndef SOPHUS_RXSO3_HPP
24 | #define RXSO3_HPP
25 |
26 | #include "sophus.hpp"
27 | #include "so3.hpp"
28 |
29 | ////////////////////////////////////////////////////////////////////////////
30 | // Forward Declarations / typedefs
31 | ////////////////////////////////////////////////////////////////////////////
32 |
33 | namespace Sophus {
34 | template class RxSO3Group;
35 | typedef RxSO3Group ScSO3 EIGEN_DEPRECATED;
36 | typedef RxSO3Group RxSO3d; /**< double precision RxSO3 */
37 | typedef RxSO3Group RxSO3f; /**< single precision RxSO3 */
38 | }
39 |
40 | ////////////////////////////////////////////////////////////////////////////
41 | // Eigen Traits (For querying derived types in CRTP hierarchy)
42 | ////////////////////////////////////////////////////////////////////////////
43 |
44 | namespace Eigen {
45 | namespace internal {
46 |
47 | template
48 | struct traits > {
49 | typedef _Scalar Scalar;
50 | typedef Quaternion QuaternionType;
51 | };
52 |
53 | template
54 | struct traits