12 #include <ros/console.h>
17 #include <sys/resource.h>
19 #include <opencv2/ml.hpp>
20 #include <pcl/point_types.h>
21 #include <pcl/point_cloud.h>
22 #include <pcl/segmentation/extract_clusters.h>
24 #include <Eigen/Dense>
27 #include <bmm/bayesian_mixture_model.hpp>
58 constexpr
double MIN_RETENTION_RATIO = 0.10;
66 DepthRenderer(cv::Mat& z_buffer_)
67 :
geo::RenderResult(z_buffer_.cols, z_buffer_.rows), z_buffer(z_buffer_)
73 float& old_depth = z_buffer.at<
float>(y, x);
74 if (old_depth == 0 || depth < old_depth)
88 const geo::Pose3D& sensor_pose,
double background_padding)
95 DepthRenderer res(depth_model);
99 if (!e->visual() || !e->has_pose())
103 opt.
setMesh(e->visual()->getMesh(), sensor_pose.
inverse() * e->pose());
111 for(
unsigned int i = 0; i < size; ++i)
114 float dm = depth_model.at<
float>(i);
115 if (dm > 0 && ds > 0 && ds > dm - background_padding)
129 min_buffer = cv::Mat(height, width, CV_32FC1, 0.0);
130 max_buffer = cv::Mat(height, width, CV_32FC1, 0.0);
144 if (d_min == 0 || depth < d_min)
158 const geo::Pose3D& shape_pose, cv::Mat& filtered_depth_image)
const
174 cam_model.
render(opt, res);
187 if (d_min > 0 && d_max > 0 && d >= d_min && d <= d_max)
188 filtered_depth_image.at<
float>(i) = d;
195 const cv::Mat& filtered_depth_image)
const
198 for (
int y = 0; y <
rgb_image.rows; ++y) {
199 const float* depth_row = filtered_depth_image.ptr<
float>(y);
200 cv::Vec3b* out_row = masked_rgb.ptr<cv::Vec3b>(y);
201 const cv::Vec3b* rgb_row =
rgb_image.ptr<cv::Vec3b>(y);
202 for (
int x = 0; x <
rgb_image.cols; ++x) {
203 if (depth_row[x] > 0.f) {
204 out_row[x] = rgb_row[x];
218 ROS_DEBUG(
"Cluster with depth image of size %i, %i", width, height);
227 if (i_space != std::string::npos)
229 area_name = area_description.
substr(0, i_space);
230 area_entity = area_description.
substr(i_space + 1);
233 ROS_DEBUG(
"Creating clusters");
253 #pragma omp parallel for schedule(dynamic)
254 for (
size_t i = 0; i < masks.
size(); ++i)
257 const cv::Mat& mask_orig = masks[i];
260 if (mask_orig.empty())
262 ROS_DEBUG(
"Skipping cluster %zu: SAM returned empty mask (segmentation failure for this box)", i);
268 if (area_name ==
"on_top_of" && i < seg_result.
labels.
size())
273 ROS_WARN(
"Skipping cluster %zu: label '%s' is the supporting surface for area '%s' and area description '%s'",
274 i, seg_result.
labels[i].c_str(), area_name.
c_str(), area_description.
c_str());
280 if (mask_orig.rows != height || mask_orig.cols != width)
281 cv::resize(mask_orig, mask, cv::Size(width, height), 0, 0, cv::INTER_NEAREST);
288 for(
int y = 0; y < mask.rows; ++y) {
289 for(
int x = 0; x < mask.cols; ++x) {
290 if (mask.at<
unsigned char>(y, x) > 0) {
291 unsigned int pixel_idx = y * width + x;
295 cluster.pixel_indices.push_back(pixel_idx);
303 if (
cluster.pixel_indices.size() < MIN_CLUSTER_POINTS) {
307 ROS_WARN(
"We reject cluster %zu with label '%s' because it has only %zu points", i, label.
c_str(),
cluster.pixel_indices.size());
315 ROS_WARN(
"Cluster %zu with label '%s': %zu points", i, label.
c_str(),
cluster.points.size());
319 MAPGMM gmm(2,
cluster.points, params);
320 gmm.fit(
cluster.points, sensor_pose);
323 int inlier_component = gmm.get_inlier_component();
329 for (
size_t j = 0; j < labels.
size(); j++)
331 if (labels[j] == inlier_component)
342 if (filtered_points.
size() > MIN_FILTERED_POINTS &&
343 filtered_points.
size() > MIN_RETENTION_RATIO *
cluster.points.size()) {
345 cluster.points = filtered_points;
348 cluster.outlier_points = outlier_points;
359 ROS_DEBUG(
"GMM filtering rejected: retained %zu/%zu points",
368 for(
unsigned int j = 0; j <
cluster.points.size(); ++j)
375 z_min = std::min<float>(z_min, p_map.
z);
376 z_max = std::max<float>(z_max, p_map.
z);
380 cluster.chull.complete =
false;
389 ROS_INFO(
"Cluster %zu classified as '%s' with confidence %.2f", i,
cluster.label.c_str(),
cluster.classification_confidence);
395 valid_cluster[i] =
true;
401 for (
size_t i = 0; i < temp_clusters.
size(); ++i) {
402 if (valid_cluster[i]) {
403 clusters.
push_back(std::move(temp_clusters[i]));