Skip to content

Commit b58ef4a

Browse files
authored
Impl noise filtering layer in the costmap_2d (ros-navigation#2567)
Signed-off-by: ryzhikovas <[email protected]>
1 parent 3cfc15e commit b58ef4a

11 files changed

+2876
-0
lines changed

nav2_costmap_2d/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ add_library(layers SHARED
8787
src/observation_buffer.cpp
8888
plugins/voxel_layer.cpp
8989
plugins/range_sensor_layer.cpp
90+
plugins/denoise_layer.cpp
9091
)
9192
ament_target_dependencies(layers
9293
${dependencies}

nav2_costmap_2d/costmap_plugins.xml

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
<class type="nav2_costmap_2d::RangeSensorLayer" base_class_type="nav2_costmap_2d::Layer">
1616
<description>A range-sensor (sonar, IR) based obstacle layer for costmap_2d</description>
1717
</class>
18+
<class type="nav2_costmap_2d::DenoiseLayer" base_class_type="nav2_costmap_2d::Layer">
19+
<description>Filters noise-induced freestanding obstacles or small obstacles groups</description>
20+
</class>
1821
</library>
1922

2023
<library path="filters">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
// Copyright (c) 2023 Andrey Ryzhikov
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef NAV2_COSTMAP_2D__DENOISE__IMAGE_HPP_
16+
#define NAV2_COSTMAP_2D__DENOISE__IMAGE_HPP_
17+
18+
#include <cstddef>
19+
#include <stdexcept>
20+
21+
namespace nav2_costmap_2d
22+
{
23+
24+
/**
25+
* @brief Image with pixels of type T
26+
* Сan own data, be a wrapper over some memory buffer, or refer to a fragment of another image
27+
* Pixels of one row are stored continuity. But rows continuity is not guaranteed.
28+
* The distance (number of elements of type T) from row(i) to row(i + 1) is equal to step()
29+
* @tparam T type of pixel
30+
*/
31+
template<class T>
32+
class Image
33+
{
34+
public:
35+
/// @brief Create empty image
36+
Image() = default;
37+
38+
/**
39+
* @brief Create image referencing to a third-party buffer
40+
* @param rows number of image rows
41+
* @param columns number of image columns
42+
* @param data existing memory buffer with size at least rows * columns
43+
* @param step offset from row(i) to row(i + 1) in memory buffer (number of elements of type T).
44+
* offset = columns if rows are stored continuity
45+
*/
46+
Image(size_t rows, size_t columns, T * data, size_t step);
47+
48+
/**
49+
* @brief Create image referencing to the other
50+
* Share image data between new and old object.
51+
* Changing data in a new object will affect the given one and vice versa
52+
*/
53+
Image(Image & other);
54+
55+
/**
56+
* @brief Create image from the other (move constructor)
57+
*/
58+
Image(Image && other) noexcept;
59+
60+
/// @return number of image rows
61+
size_t rows() const {return rows_;}
62+
63+
/// @return number of image columns
64+
size_t columns() const {return columns_;}
65+
66+
/// @return true if image empty
67+
bool empty() const {return rows_ == 0 || columns_ == 0;}
68+
69+
/// @return offset (number of elements of type T) from row(i) to row(i + 1)
70+
size_t step() const {return step_;}
71+
72+
/**
73+
* @return pointer to first pixel of row
74+
* @warning If row >= rows(), the behavior is undefined
75+
*/
76+
T * row(size_t row);
77+
78+
/// @overload
79+
const T * row(size_t row) const;
80+
81+
/**
82+
* @brief Read (and modify, if need) each pixel sequentially
83+
* @tparam Functor function object.
84+
* Signature should be equivalent to the following:
85+
* void fn(T& pixel) or void fn(const T& pixel)
86+
* @param fn a function that will be applied to each pixel in the image. Can modify image data
87+
*/
88+
template<class Functor>
89+
void forEach(Functor && fn);
90+
91+
/**
92+
* @brief Read each pixel sequentially
93+
* @tparam Functor function object.
94+
* Signature should be equivalent to the following:
95+
* void fn(const T& pixel)
96+
* @param fn a function that will be applied to each pixel in the image
97+
*/
98+
template<class Functor>
99+
void forEach(Functor && fn) const;
100+
/**
101+
* @brief Convert each pixel to corresponding pixel of target using a custom function
102+
*
103+
* The source and target must be the same size.
104+
* For calculation of new target value the operation can use source value and
105+
* an optionally current target value.
106+
* This function call operation(this[i, j], target[i, j]) for each pixel
107+
* where target[i, j] is mutable
108+
* @tparam TargetElement type of target pixel
109+
* @tparam Converter function object.
110+
* Signature should be equivalent to the following:
111+
* void fn(const T& src, TargetElement& trg)
112+
* @param target output image with TargetElement-type pixels
113+
* @param operation the binary operation op is applied to pairs of pixels:
114+
* first (const) from source and second (mutable) from target
115+
* @throw std::logic_error if the source and target of different sizes
116+
*/
117+
template<class TargetElement, class Converter>
118+
void convert(Image<TargetElement> & target, Converter && converter) const;
119+
120+
private:
121+
T * data_start_{};
122+
size_t rows_{};
123+
size_t columns_{};
124+
size_t step_{};
125+
};
126+
127+
template<class T>
128+
Image<T>::Image(size_t rows, size_t columns, T * data, size_t step)
129+
: rows_{rows}, columns_{columns}, step_{step}
130+
{
131+
data_start_ = data;
132+
}
133+
134+
template<class T>
135+
Image<T>::Image(Image & other)
136+
: data_start_{other.data_start_},
137+
rows_{other.rows_}, columns_{other.columns_}, step_{other.step_} {}
138+
139+
template<class T>
140+
Image<T>::Image(Image && other) noexcept
141+
: data_start_{other.data_start_},
142+
rows_{other.rows_}, columns_{other.columns_}, step_{other.step_} {}
143+
144+
template<class T>
145+
T * Image<T>::row(size_t row)
146+
{
147+
return const_cast<T *>( static_cast<const Image<T> &>(*this).row(row) );
148+
}
149+
150+
template<class T>
151+
const T * Image<T>::row(size_t row) const
152+
{
153+
return data_start_ + row * step_;
154+
}
155+
156+
template<class T>
157+
template<class Functor>
158+
void Image<T>::forEach(Functor && fn)
159+
{
160+
static_cast<const Image<T> &>(*this).forEach(
161+
[&](const T & pixel) {
162+
fn(const_cast<T &>(pixel));
163+
});
164+
}
165+
166+
template<class T>
167+
template<class Functor>
168+
void Image<T>::forEach(Functor && fn) const
169+
{
170+
const T * rowPtr = row(0);
171+
172+
for (size_t row = 0; row < rows(); ++row) {
173+
const T * rowEnd = rowPtr + columns();
174+
175+
for (const T * pixel = rowPtr; pixel != rowEnd; ++pixel) {
176+
fn(*pixel);
177+
}
178+
rowPtr += step();
179+
}
180+
}
181+
182+
template<class T>
183+
template<class TargetElement, class Converter>
184+
void Image<T>::convert(Image<TargetElement> & target, Converter && converter) const
185+
{
186+
if (rows() != target.rows() || columns() != target.columns()) {
187+
throw std::logic_error("Image::convert. The source and target images size are different");
188+
}
189+
const T * source_row = row(0);
190+
TargetElement * target_row = target.row(0);
191+
192+
for (size_t row = 0; row < rows(); ++row) {
193+
const T * rowInEnd = source_row + columns();
194+
const T * src = source_row;
195+
TargetElement * trg = target_row;
196+
197+
for (; src != rowInEnd; ++src, ++trg) {
198+
converter(*src, *trg);
199+
}
200+
source_row += step();
201+
target_row += target.step();
202+
}
203+
}
204+
205+
} // namespace nav2_costmap_2d
206+
207+
#endif // NAV2_COSTMAP_2D__DENOISE__IMAGE_HPP_

0 commit comments

Comments
 (0)