Skip to content

Commit dccb921

Browse files
committed
option to use ClusterPointIndices for outlier removal
1 parent be6e319 commit dccb921

File tree

2 files changed

+183
-46
lines changed

2 files changed

+183
-46
lines changed

jsk_pcl_ros/include/jsk_pcl_ros/organized_statistical_outlier_removal.h

+30-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@
4242
#include <jsk_topic_tools/diagnostic_nodelet.h>
4343
#include <jsk_topic_tools/counter.h>
4444
#include <dynamic_reconfigure/server.h>
45+
#include <message_filters/subscriber.h>
46+
#include <message_filters/time_synchronizer.h>
47+
#include <message_filters/synchronizer.h>
48+
49+
#include "sensor_msgs/PointCloud2.h"
50+
#include "jsk_recognition_msgs/ClusterPointIndices.h"
51+
4552
#include "jsk_pcl_ros/OrganizedStatisticalOutlierRemovalConfig.h"
4653

4754
namespace jsk_pcl_ros
@@ -50,6 +57,12 @@ namespace jsk_pcl_ros
5057
{
5158
public:
5259
typedef jsk_pcl_ros::OrganizedStatisticalOutlierRemovalConfig Config;
60+
typedef message_filters::sync_policies::ExactTime<
61+
sensor_msgs::PointCloud2,
62+
jsk_recognition_msgs::ClusterPointIndices > SyncPolicy;
63+
typedef message_filters::sync_policies::ApproximateTime<
64+
sensor_msgs::PointCloud2,
65+
jsk_recognition_msgs::ClusterPointIndices > ApproximateSyncPolicy;
5366
typedef pcl::PointXYZRGB PointT;
5467
OrganizedStatisticalOutlierRemoval();
5568
protected:
@@ -64,7 +77,15 @@ namespace jsk_pcl_ros
6477

6578
virtual void configCallback (Config &config, uint32_t level);
6679

67-
virtual void filter(const sensor_msgs::PointCloud2::ConstPtr& msg);
80+
virtual void filter(
81+
const pcl::PointCloud<PointT>::Ptr cloud,
82+
pcl::PointCloud<PointT>::Ptr cloud_filtered,
83+
bool keep_organized);
84+
virtual void filterCloud(const sensor_msgs::PointCloud2::ConstPtr& msg);
85+
virtual void filterCloudWithClusterPointIndices(
86+
const sensor_msgs::PointCloud2::ConstPtr& msg,
87+
const jsk_recognition_msgs::ClusterPointIndices::ConstPtr& cpi_msg);
88+
6889

6990
virtual void updateDiagnostic(
7091
diagnostic_updater::DiagnosticStatusWrapper &stat);
@@ -73,6 +94,11 @@ namespace jsk_pcl_ros
7394
// ROS variables
7495
////////////////////////////////////////////////////////
7596
ros::Subscriber sub_;
97+
message_filters::Subscriber<sensor_msgs::PointCloud2> sub_cloud_;
98+
message_filters::Subscriber<jsk_recognition_msgs::ClusterPointIndices> sub_cpi_;
99+
boost::shared_ptr<message_filters::Synchronizer<SyncPolicy> >sync_;
100+
boost::shared_ptr<message_filters::Synchronizer<ApproximateSyncPolicy> >async_;
101+
76102
ros::Publisher pub_;
77103
boost::shared_ptr <dynamic_reconfigure::Server<Config> > srv_;
78104
boost::mutex mutex_;
@@ -84,6 +110,9 @@ namespace jsk_pcl_ros
84110
////////////////////////////////////////////////////////
85111
// Parameters
86112
////////////////////////////////////////////////////////
113+
bool use_cpi_;
114+
bool use_async_;
115+
int queue_size_;
87116
bool keep_organized_;
88117
bool negative_;
89118
double std_mul_;

jsk_pcl_ros/src/organized_statistical_outlier_removal_nodelet.cpp

+153-45
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ namespace jsk_pcl_ros
5151
{
5252
DiagnosticNodelet::onInit();
5353
pub_ = advertise<sensor_msgs::PointCloud2>(*pnh_, "output", 1);
54+
pnh_->param("use_cluster_point_indices", use_cpi_, false);
55+
pnh_->param("approximate_sync", use_async_, false);
56+
pnh_->param("queue_size", queue_size_, 100);
5457

5558
srv_ = boost::make_shared <dynamic_reconfigure::Server<Config> > (*pnh_);
5659
dynamic_reconfigure::Server<Config>::CallbackType f =
@@ -62,7 +65,31 @@ namespace jsk_pcl_ros
6265

6366
void OrganizedStatisticalOutlierRemoval::subscribe()
6467
{
65-
sub_ = pnh_->subscribe("input", 1, &OrganizedStatisticalOutlierRemoval::filter, this);
68+
if (use_cpi_)
69+
{
70+
sub_cloud_.subscribe(*pnh_, "input", 1);
71+
sub_cpi_.subscribe(*pnh_, "input/cluster_indices", 1);
72+
if (use_async_)
73+
{
74+
async_ = boost::make_shared<message_filters::Synchronizer<ApproximateSyncPolicy> >(queue_size_);
75+
async_->connectInput(sub_cloud_, sub_cpi_);
76+
async_->registerCallback(
77+
boost::bind(
78+
&OrganizedStatisticalOutlierRemoval::filterCloudWithClusterPointIndices, this, _1, _2));
79+
}
80+
else
81+
{
82+
sync_ = boost::make_shared<message_filters::Synchronizer<SyncPolicy> >(queue_size_);
83+
sync_->connectInput(sub_cloud_, sub_cpi_);
84+
sync_->registerCallback(
85+
boost::bind(
86+
&OrganizedStatisticalOutlierRemoval::filterCloudWithClusterPointIndices, this, _1, _2));
87+
}
88+
}
89+
else
90+
{
91+
sub_ = pnh_->subscribe("input", 1, &OrganizedStatisticalOutlierRemoval::filterCloud, this);
92+
}
6693
}
6794

6895
void OrganizedStatisticalOutlierRemoval::unsubscribe()
@@ -79,26 +106,16 @@ namespace jsk_pcl_ros
79106
std_mul_ = config.stddev;
80107
}
81108

82-
void OrganizedStatisticalOutlierRemoval::filter(const sensor_msgs::PointCloud2::ConstPtr& msg)
109+
void OrganizedStatisticalOutlierRemoval::filter(
110+
pcl::PointCloud<PointT>::Ptr cloud,
111+
pcl::PointCloud<PointT>::Ptr cloud_filtered,
112+
bool keep_organized)
83113
{
84-
boost::mutex::scoped_lock lock(mutex_);
85-
vital_checker_->poke();
86-
sensor_msgs::PointCloud2 output;
87-
88-
if (keep_organized_&& msg->is_dense) {
89-
NODELET_ERROR("keep_organized parameter is true, but input pointcloud is not organized.");
90-
}
91-
bool keep_organized = keep_organized_ && !msg->is_dense;
92-
93114
#if PCL_VERSION_COMPARE (<, 1, 9, 0)
94115
if (keep_organized) {
95-
// Send the input dataset to the spatial locator
96-
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
97-
pcl::fromROSMsg(*msg, *cloud);
98-
99116
// Initialize the spatial locator
100-
pcl::search::Search<pcl::PointXYZ>::Ptr tree;
101-
tree.reset (new pcl::search::OrganizedNeighbor<pcl::PointXYZ> ());
117+
pcl::search::Search<PointT>::Ptr tree;
118+
tree.reset (new pcl::search::OrganizedNeighbor<PointT> ());
102119
tree->setInputCloud (cloud);
103120

104121
// Allocate enough space to hold the results
@@ -147,74 +164,165 @@ namespace jsk_pcl_ros
147164

148165
double distance_threshold = mean + std_mul_ * stddev; // a distance that is bigger than this signals an outlier
149166

150-
// Copy the common fields
151-
output.is_bigendian = msg->is_bigendian;
152-
output.fields = msg->fields;
153-
output.point_step = msg->point_step;
154-
output.data.resize (msg->data.size ());
167+
pcl::PCLPointCloud2::Ptr pcl_cloud (new pcl::PCLPointCloud2);
168+
pcl::PCLPointCloud2::Ptr pcl_cloud_filtered (new pcl::PCLPointCloud2);
169+
pcl::toPCLPointCloud2(*cloud, *pcl_cloud);
170+
pcl_cloud_filtered->is_bigendian = pcl_cloud->is_bigendian;
171+
pcl_cloud_filtered->fields = pcl_cloud->fields;
172+
pcl_cloud_filtered->point_step = pcl_cloud->point_step;
173+
pcl_cloud_filtered->data.resize (pcl_cloud->data.size ());
174+
pcl_cloud_filtered->width = pcl_cloud->width;
175+
pcl_cloud_filtered->height = pcl_cloud->height;
176+
pcl_cloud_filtered->row_step = pcl_cloud_filtered->point_step * pcl_cloud_filtered->width;
155177

156178
// Build a new cloud by neglecting outliers
157179
int nr_p = 0;
158-
int nr_removed_p = 0;
159-
bool remove_point = false;
160180
for (int cp = 0; cp < static_cast<int> (indices.size ()); ++cp)
161181
{
182+
bool remove_point = false;
162183
if (negative_)
163184
remove_point = (distances[cp] <= distance_threshold);
164185
else
165186
remove_point = (distances[cp] > distance_threshold);
166187
if (remove_point)
167188
{
168189
/* Set the current point to NaN. */
169-
*(reinterpret_cast<float*>(&output.data[nr_p * output.point_step])+0) = std::numeric_limits<float>::quiet_NaN();
170-
*(reinterpret_cast<float*>(&output.data[nr_p * output.point_step])+1) = std::numeric_limits<float>::quiet_NaN();
171-
*(reinterpret_cast<float*>(&output.data[nr_p * output.point_step])+2) = std::numeric_limits<float>::quiet_NaN();
172-
nr_p++;
190+
*(reinterpret_cast<float*>(&pcl_cloud_filtered->data[nr_p * pcl_cloud_filtered->point_step])+0) = std::numeric_limits<float>::quiet_NaN();
191+
*(reinterpret_cast<float*>(&pcl_cloud_filtered->data[nr_p * pcl_cloud_filtered->point_step])+1) = std::numeric_limits<float>::quiet_NaN();
192+
*(reinterpret_cast<float*>(&pcl_cloud_filtered->data[nr_p * pcl_cloud_filtered->point_step])+2) = std::numeric_limits<float>::quiet_NaN();
173193
}
174194
else
175195
{
176-
memcpy (&output.data[nr_p * output.point_step],
177-
&msg->data[indices[cp] * output.point_step],
178-
output.point_step);
179-
nr_p++;
196+
memcpy (&pcl_cloud_filtered->data[nr_p * pcl_cloud_filtered->point_step],
197+
&pcl_cloud->data[indices[cp] * pcl_cloud_filtered->point_step],
198+
pcl_cloud_filtered->point_step);
180199
}
200+
nr_p++;
181201
}
182-
output.width = msg->width;
183-
output.height = msg->height;
184-
output.row_step = output.point_step * output.width;
202+
pcl::fromPCLPointCloud2(*pcl_cloud_filtered, *cloud_filtered);
185203
}
186204
else
187205
{
188-
pcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>);
189-
pcl::PointCloud<PointT>::Ptr cloud_filtered (new pcl::PointCloud<PointT>);
190-
pcl::fromROSMsg(*msg, *cloud);
191206
pcl::StatisticalOutlierRemoval<PointT> sor;
192207
sor.setInputCloud(cloud);
193208
sor.setMeanK (mean_k_);
194209
sor.setStddevMulThresh (std_mul_);
195210
sor.setNegative (negative_);
196211
sor.filter (*cloud_filtered);
197-
pcl::toROSMsg(*cloud_filtered, output);
198212
}
199-
output.header = msg->header;
200-
output.is_dense = !keep_organized;
201-
pub_.publish(output);
202213
#else
203-
pcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>);
204-
pcl::PointCloud<PointT>::Ptr cloud_filtered (new pcl::PointCloud<PointT>);
205-
pcl::fromROSMsg(*msg, *cloud);
206214
pcl::StatisticalOutlierRemoval<PointT> sor;
207215
sor.setInputCloud(cloud);
208216
sor.setMeanK (mean_k_);
209217
sor.setStddevMulThresh (std_mul_);
210218
sor.setNegative (negative_);
211219
sor.setKeepOrganized (keep_organized);
212220
sor.filter (*cloud_filtered);
221+
#endif
222+
}
223+
224+
void OrganizedStatisticalOutlierRemoval::filterCloud(const sensor_msgs::PointCloud2::ConstPtr& msg)
225+
{
226+
boost::mutex::scoped_lock lock(mutex_);
227+
vital_checker_->poke();
228+
sensor_msgs::PointCloud2 output;
229+
230+
if (keep_organized_&& msg->is_dense) {
231+
NODELET_ERROR("keep_organized parameter is true, but input pointcloud is not organized.");
232+
}
233+
bool keep_organized = keep_organized_ && !msg->is_dense;
234+
pcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>);
235+
pcl::PointCloud<PointT>::Ptr cloud_filtered (new pcl::PointCloud<PointT>);
236+
pcl::fromROSMsg(*msg, *cloud);
237+
238+
// filter
239+
OrganizedStatisticalOutlierRemoval::filter(cloud, cloud_filtered, keep_organized);
240+
213241
pcl::toROSMsg(*cloud_filtered, output);
242+
#if PCL_VERSION_COMPARE (<, 1, 9, 0)
243+
if (keep_organized) {
244+
// Copy the common fields
245+
output.is_bigendian = msg->is_bigendian;
246+
output.fields = msg->fields;
247+
output.point_step = msg->point_step;
248+
output.data.resize (msg->data.size ());
249+
output.width = msg->width;
250+
output.height = msg->height;
251+
output.row_step = msg->point_step * msg->width;
252+
}
253+
#endif
214254
output.header = msg->header;
215255
output.is_dense = !keep_organized;
216256
pub_.publish(output);
257+
diagnostic_updater_->update();
258+
}
259+
260+
void OrganizedStatisticalOutlierRemoval::filterCloudWithClusterPointIndices(
261+
const sensor_msgs::PointCloud2::ConstPtr& msg,
262+
const jsk_recognition_msgs::ClusterPointIndices::ConstPtr& cpi_msg)
263+
{
264+
boost::mutex::scoped_lock lock(mutex_);
265+
vital_checker_->poke();
266+
sensor_msgs::PointCloud2 output;
267+
268+
if (keep_organized_&& msg->is_dense) {
269+
NODELET_ERROR("keep_organized parameter is true, but input pointcloud is not organized.");
270+
}
271+
bool keep_organized = keep_organized_ && !msg->is_dense;
272+
pcl::PointCloud<PointT>::Ptr cloud (new pcl::PointCloud<PointT>);
273+
pcl::PointCloud<PointT>::Ptr cloud_filtered (new pcl::PointCloud<PointT>);
274+
pcl::fromROSMsg(*msg, *cloud);
275+
cloud_filtered->points.resize(cloud->points.size());
276+
PointT nan_point;
277+
nan_point.x = nan_point.y = nan_point.z = std::numeric_limits<float>::quiet_NaN();
278+
std::fill(cloud_filtered->points.begin(), cloud_filtered->points.end(), nan_point);
279+
280+
pcl::ExtractIndices<PointT> ex;
281+
ex.setInputCloud(cloud);
282+
ex.setKeepOrganized(keep_organized);
283+
ex.setNegative(false);
284+
285+
for (size_t i = 0; i < cpi_msg->cluster_indices.size(); i++)
286+
{
287+
pcl::IndicesPtr indices;
288+
pcl::PointCloud<PointT>::Ptr cluster_cloud(new pcl::PointCloud<PointT>);
289+
pcl::PointCloud<PointT>::Ptr cluster_cloud_filtered(new pcl::PointCloud<PointT>);
290+
indices.reset (new std::vector<int> (cpi_msg->cluster_indices[i].indices));
291+
ex.setIndices(indices);
292+
ex.filter(*cluster_cloud);
293+
OrganizedStatisticalOutlierRemoval::filter(cluster_cloud, cluster_cloud_filtered, keep_organized);
294+
for (size_t j = 0; j < indices->size(); j++)
295+
{
296+
int ind = (*indices)[j];
297+
if (!std::isnan(cluster_cloud_filtered->points[ind].x) &&
298+
!std::isnan(cluster_cloud_filtered->points[ind].y) &&
299+
!std::isnan(cluster_cloud_filtered->points[ind].z))
300+
{
301+
cloud_filtered->points[ind].x = cluster_cloud_filtered->points[ind].x;
302+
cloud_filtered->points[ind].y = cluster_cloud_filtered->points[ind].y;
303+
cloud_filtered->points[ind].z = cluster_cloud_filtered->points[ind].z;
304+
cloud_filtered->points[ind].rgb = cluster_cloud_filtered->points[ind].rgb;
305+
}
306+
}
307+
308+
}
309+
310+
pcl::toROSMsg(*cloud_filtered, output);
311+
#if PCL_VERSION_COMPARE (<, 1, 9, 0)
312+
if (keep_organized) {
313+
// Copy the common fields
314+
output.is_bigendian = msg->is_bigendian;
315+
output.fields = msg->fields;
316+
output.point_step = msg->point_step;
317+
output.data.resize (msg->data.size ());
318+
output.width = msg->width;
319+
output.height = msg->height;
320+
output.row_step = output.point_step * output.width;
321+
}
217322
#endif
323+
output.header = msg->header;
324+
output.is_dense = !keep_organized;
325+
pub_.publish(output);
218326
diagnostic_updater_->update();
219327
}
220328

0 commit comments

Comments
 (0)