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>
46 ROS_INFO(
"Starting async initialization of segmentation pipeline (YOLO & SAM)...");
51 ROS_INFO(
"Async initialization of segmentation pipeline is complete!");
71 constexpr
double MIN_RETENTION_RATIO = 0.10;
79 DepthRenderer(cv::Mat& z_buffer_)
80 :
geo::RenderResult(z_buffer_.cols, z_buffer_.rows), z_buffer(z_buffer_)
86 float& old_depth = z_buffer.at<
float>(y, x);
87 if (old_depth == 0 || depth < old_depth)
101 const geo::Pose3D& sensor_pose,
double background_padding)
108 DepthRenderer res(depth_model);
112 if (!e->visual() || !e->has_pose())
116 opt.
setMesh(e->visual()->getMesh(), sensor_pose.
inverse() * e->pose());
124 for(
unsigned int i = 0; i < size; ++i)
127 float dm = depth_model.at<
float>(i);
128 if (dm > 0 && ds > 0 && ds > dm - background_padding)
142 min_buffer = cv::Mat(height, width, CV_32FC1, 0.0);
143 max_buffer = cv::Mat(height, width, CV_32FC1, 0.0);
157 if (d_min == 0 || depth < d_min)
171 const geo::Pose3D& shape_pose, cv::Mat& filtered_depth_image)
const
187 cam_model.
render(opt, res);
200 if (d_min > 0 && d_max > 0 && d >= d_min && d <= d_max)
201 filtered_depth_image.at<
float>(i) = d;
208 const cv::Mat& filtered_depth_image)
const
211 for (
int y = 0; y <
rgb_image.rows; ++y) {
212 const float* depth_row = filtered_depth_image.ptr<
float>(y);
213 cv::Vec3b* out_row = masked_rgb.ptr<cv::Vec3b>(y);
214 const cv::Vec3b* rgb_row =
rgb_image.ptr<cv::Vec3b>(y);
215 for (
int x = 0; x <
rgb_image.cols; ++x) {
216 if (depth_row[x] > 0.f) {
217 out_row[x] = rgb_row[x];
231 ROS_DEBUG(
"Cluster with depth image of size %i, %i", width, height);
261 ROS_DEBUG(
"Creating clusters");
281 #pragma omp parallel for schedule(dynamic)
282 for (
size_t i = 0; i < masks.
size(); ++i)
285 const cv::Mat& mask_orig = masks[i];
288 if (mask_orig.empty())
290 ROS_DEBUG(
"Skipping cluster %zu: SAM returned empty mask (segmentation failure for this box)", i);
296 if (area_name ==
"on_top_of" && i < seg_result.
labels.
size())
301 ROS_WARN(
"Skipping cluster %zu: label '%s' is the supporting surface for area '%s' and area description '%s'",
302 i, seg_result.
labels[i].c_str(), area_name.
c_str(), area_description.
c_str());
308 if (mask_orig.rows != height || mask_orig.cols != width)
309 cv::resize(mask_orig, mask, cv::Size(width, height), 0, 0, cv::INTER_NEAREST);
316 for(
int y = 0; y < mask.rows; ++y) {
317 for(
int x = 0; x < mask.cols; ++x) {
318 if (mask.at<
unsigned char>(y, x) > 0) {
319 unsigned int pixel_idx = y * width + x;
323 cluster.pixel_indices.push_back(pixel_idx);
331 if (
cluster.pixel_indices.size() < MIN_CLUSTER_POINTS) {
335 ROS_WARN(
"We reject cluster %zu with label '%s' because it has only %zu points", i, label.
c_str(),
cluster.pixel_indices.size());
343 ROS_WARN(
"Cluster %zu with label '%s': %zu points", i, label.
c_str(),
cluster.points.size());
349 MAPGMM gmm(2,
cluster.points, params);
350 gmm.fit(
cluster.points, sensor_pose);
357 int inlier_component = gmm.get_inlier_component();
363 for (
size_t j = 0; j < labels.
size(); j++)
365 if (labels[j] == inlier_component)
376 if (filtered_points.
size() > MIN_FILTERED_POINTS &&
377 filtered_points.
size() > MIN_RETENTION_RATIO *
cluster.points.size()) {
379 cluster.points = filtered_points;
382 cluster.outlier_points = outlier_points;
393 ROS_DEBUG(
"GMM filtering rejected: retained %zu/%zu points",
402 for(
unsigned int j = 0; j <
cluster.points.size(); ++j)
409 z_min = std::min<float>(z_min, p_map.
z);
410 z_max = std::max<float>(z_max, p_map.
z);
414 cluster.chull.complete =
false;
423 ROS_INFO(
"Cluster %zu classified as '%s' with confidence %.2f", i,
cluster.label.c_str(),
cluster.classification_confidence);
429 valid_cluster[i] =
true;
435 for (
size_t i = 0; i < temp_clusters.
size(); ++i) {
436 if (valid_cluster[i]) {
437 clusters.
push_back(std::move(temp_clusters[i]));
444 double bmm_total_ms = 0.0;
445 double bmm_max_ms = 0.0;
447 for (
size_t i = 0; i < bmm_latencies_ms.
size(); ++i)
449 if (bmm_latencies_ms[i] > 0.0)
451 bmm_total_ms += bmm_latencies_ms[i];
452 bmm_max_ms =
std::max(bmm_max_ms, bmm_latencies_ms[i]);
456 double bmm_avg_ms = (bmm_count > 0) ? (bmm_total_ms / bmm_count) : 0.0;
458 ROS_WARN(
"\n=== BMM Latency (parallel execution) ===\n");
459 ROS_WARN(
" BMM fits: %d masks\n", bmm_count);
460 ROS_WARN(
" BMM avg: %7.2f ms/mask\n", bmm_avg_ms);
461 ROS_WARN(
" BMM max: %7.2f ms (slowest mask)\n", bmm_max_ms);
462 ROS_WARN(
" BMM total: %7.2f ms (sequential equivalent)\n", bmm_total_ms);