ed_sensor_integration
src/kinect/segmenter.cpp
Go to the documentation of this file.
1 #include "ed/kinect/segmenter.h"
2 
4 #include <geolib/Shape.h>
5 
6 #include <rgbd/image.h>
7 #include <rgbd/view.h>
8 
9 #include <ed/world_model.h>
10 #include <ed/entity.h>
11 
12 #include <ros/console.h>
13 
14 // Clustering
15 #include <queue>
16 #include <ed/convex_hull_calc.h>
17 
18 // Visualization
19 //#include <opencv2/highgui/highgui.hpp>
20 
21 // ----------------------------------------------------------------------------------------------------
22 
24 {
25 }
26 
27 // ----------------------------------------------------------------------------------------------------
28 
30 {
31 }
32 
33 // ----------------------------------------------------------------------------------------------------
34 
35 namespace
36 {
37 
38 class DepthRenderer : public geo::RenderResult
39 {
40 
41 public:
42 
43  DepthRenderer(cv::Mat& z_buffer_)
44  : geo::RenderResult(z_buffer_.cols, z_buffer_.rows), z_buffer(z_buffer_)
45  {
46  }
47 
48  void renderPixel(int x, int y, float depth, int /*i_triangle*/)
49  {
50  float& old_depth = z_buffer.at<float>(y, x);
51  if (old_depth == 0 || depth < old_depth)
52  {
53  old_depth = depth;
54  }
55  }
56 
57  cv::Mat& z_buffer;
58 };
59 
60 }
61 
62 // ----------------------------------------------------------------------------------------------------
63 
64 void Segmenter::removeBackground(cv::Mat& depth_image, const ed::WorldModel& world, const geo::DepthCamera& cam,
65  const geo::Pose3D& sensor_pose, double background_padding)
66 {
67  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
68  // Render the world model as seen by the depth sensor
69 
70  cv::Mat depth_model(depth_image.rows, depth_image.cols, CV_32FC1, 0.0);
71 
72  DepthRenderer res(depth_model);
73  for(ed::WorldModel::const_iterator it = world.begin(); it != world.end(); ++it)
74  {
75  const ed::EntityConstPtr& e = *it;
76  if (!e->visual() || !e->has_pose())
77  continue;
78 
80  opt.setMesh(e->visual()->getMesh(), sensor_pose.inverse() * e->pose());
81  cam.render(opt, res);
82  }
83 
84  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
85  // Filter all points that can be associated with the rendered depth image
86 
87  unsigned int size = depth_image.rows * depth_image.cols;
88  for(unsigned int i = 0; i < size; ++i)
89  {
90  float& ds = depth_image.at<float>(i);
91  float dm = depth_model.at<float>(i);
92  if (dm > 0 && ds > 0 && ds > dm - background_padding)
93  ds = 0;
94  }
95 }
96 
97 // ----------------------------------------------------------------------------------------------------
98 
100 {
101 
102 public:
103 
104  MinMaxRenderer(int width, int height) : geo::RenderResult(width, height)
105  {
106  min_buffer = cv::Mat(height, width, CV_32FC1, 0.0);
107  max_buffer = cv::Mat(height, width, CV_32FC1, 0.0);
108  }
109 
110  void renderPixel(int x, int y, float depth, int /*i_triangle*/)
111  {
112  // TODO: now the renderer can only deal with convex meshes, which means
113  // that at each pixel there can only be one minimum and one maximum pixel
114  // There is an easy solution for concave meshes: determine which side
115  // the triangle points (away or to the camera) and test the pixel in the depth
116  // image to be on the correct side. ... etc ...
117 
118  float& d_min = min_buffer.at<float>(y, x);
119  float& d_max = max_buffer.at<float>(y, x);
120 
121  if (d_min == 0 || depth < d_min)
122  d_min = depth;
123 
124  d_max = std::max(d_max, depth);
125  }
126 
127  cv::Mat min_buffer;
128  cv::Mat max_buffer;
129 
130 };
131 
132 // ----------------------------------------------------------------------------------------------------
133 
135  const geo::Pose3D& shape_pose, cv::Mat& filtered_depth_image) const
136 {
137  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
138  // Render shape
139 
140  const cv::Mat& depth_image = image.getDepthImage();
141 
142  rgbd::View view(image, depth_image.cols);
143  const geo::DepthCamera& cam_model = view.getRasterizer();
144 
145  MinMaxRenderer res(depth_image.cols, depth_image.rows);
146 
147  geo::RenderOptions opt;
148  opt.setBackFaceCulling(false);
149  opt.setMesh(shape.getMesh(), shape_pose);
150 
151  cam_model.render(opt, res);
152 
153  filtered_depth_image = cv::Mat(depth_image.rows, depth_image.cols, CV_32FC1, 0.0);
154 
155  for(int i = 0; i < depth_image.cols * depth_image.rows; ++i)
156  {
157  float d = depth_image.at<float>(i);
158  if (d <= 0)
159  continue;
160 
161  float d_min = res.min_buffer.at<float>(i);
162  float d_max = res.max_buffer.at<float>(i);
163 
164  if (d_min > 0 && d_max > 0 && d >= d_min && d <= d_max)
165  filtered_depth_image.at<float>(i) = d;
166  }
167 
168 // cv::imshow("min", res.min_buffer / 10);
169 // cv::imshow("max", res.max_buffer / 10);
170 // cv::imshow("diff", (res.max_buffer - res.min_buffer) * 10);
171 // cv::imshow("filtered", filtered_depth_image / 10);
172 // cv::waitKey();
173 }
174 
175 // ----------------------------------------------------------------------------------------------------
176 
177 void Segmenter::cluster(const cv::Mat& depth_image, const geo::DepthCamera& cam_model,
178  const geo::Pose3D& sensor_pose, std::vector<EntityUpdate>& clusters) const
179 {
180  int width = depth_image.cols;
181  int height = depth_image.rows;
182  ROS_DEBUG("Cluster with depth image of size %i, %i", width, height);
183 
184  cv::Mat visited(height, width, CV_8UC1, cv::Scalar(0));
185 
186  // Mark borders as visited (2-pixel border)
187  for(int x = 0; x < width; ++x)
188  {
189  visited.at<unsigned char>(0, x) = 1;
190  visited.at<unsigned char>(1, x) = 1;
191  visited.at<unsigned char>(height - 1, x) = 1;
192  visited.at<unsigned char>(height - 2, x) = 1;
193  }
194 
195  for(int y = 0; y < height; ++y)
196  {
197  visited.at<unsigned char>(y, 0) = 1;
198  visited.at<unsigned char>(y, 1) = 1;
199  visited.at<unsigned char>(y, width - 1) = 1;
200  visited.at<unsigned char>(y, width - 2) = 1;
201  }
202 
203  int dirs[] = { -1, 1, -width, width,
204  -2, 2, -width * 2, width * 2}; // Also try one pixel skipped (filtering may cause some 1-pixel gaps)
205 
206  ROS_DEBUG("Creating clusters");
207  unsigned int size = width * height;
208  for(unsigned int i_pixel = 0; i_pixel < size; ++i_pixel)
209  {
210  float d = depth_image.at<float>(i_pixel);
211 
212  if (d == 0 || d != d)
213  continue;
214 
215  // Create cluster
216  clusters.push_back(EntityUpdate());
217  EntityUpdate& cluster = clusters.back();
218 
219  // Mark visited
220  visited.at<unsigned char>(i_pixel) = 1;
221 
223  Q.push(i_pixel);
224 
225  while(!Q.empty())
226  {
227  unsigned int p1 = Q.front();
228  Q.pop();
229 
230  float p1_d = depth_image.at<float>(p1);
231 
232  // Add to cluster
233  cluster.pixel_indices.push_back(p1);
234  cluster.points.push_back(cam_model.project2Dto3D(p1 % width, p1 / width) * p1_d);
235 
236  for(int dir = 0; dir < 8; ++dir)
237  {
238  unsigned int p2 = p1 + dirs[dir];
239 
240  // Check if out of bounds (N.B., if dirs[dir] < 0, p2 might be negative but since it's an unsigned int it will
241  // in practice be a large number.
242  if (p2 >= size)
243  continue;
244  float p2_d = depth_image.at<float>(p2);
245 
246  // If not yet visited, and depth is within bounds
247  if (visited.at<unsigned char>(p2) == 0 && std::abs<float>(p2_d - p1_d) < 0.05)
248  {
249  // Mark visited
250  visited.at<unsigned char>(p2) = 1;
251 
252  // Add point to queue
253  Q.push(p2);
254  }
255  }
256  }
257 
258  // Check if cluster has enough points. If not, remove it from the list
259  if (cluster.pixel_indices.size() < 100) // TODO: magic number
260  {
261  clusters.pop_back();
262  continue;
263  }
264 
265  // Calculate cluster convex hull
266  float z_min = 1e9;
267  float z_max = -1e9;
268 
269  // Calculate z_min and z_max of cluster
270  ROS_DEBUG("Computing min and max of cluster");
271  std::vector<geo::Vec2f> points_2d(cluster.points.size());
272  for(unsigned int j = 0; j < cluster.points.size(); ++j)
273  {
274  const geo::Vec3& p = cluster.points[j];
275 
276  // Transform sensor point to map frame
277  geo::Vector3 p_map = sensor_pose * p;
278 
279  points_2d[j] = geo::Vec2f(p_map.x, p_map.y);
280 
281  z_min = std::min<float>(z_min, p_map.z);
282  z_max = std::max<float>(z_max, p_map.z);
283  }
284 
285  ed::convex_hull::create(points_2d, z_min, z_max, cluster.chull, cluster.pose_map);
286  cluster.chull.complete = false;
287  }
288 }
geo::Vector3::y
const real & y() const
Segmenter::cluster
void cluster(const cv::Mat &depth_image, const geo::DepthCamera &cam_model, const geo::Pose3D &sensor_pose, std::vector< EntityUpdate > &clusters) const
Definition: src/kinect/segmenter.cpp:177
MinMaxRenderer::MinMaxRenderer
MinMaxRenderer(int width, int height)
Definition: src/kinect/segmenter.cpp:104
EntityUpdate
collection structure for laser entities
Definition: kinect/entity_update.h:9
DepthCamera.h
geo::RenderOptions::setMesh
void setMesh(const geo::Mesh &mesh, const geo::Pose3D &pose=geo::Pose3D::identity())
geo
ed::convex_hull::create
void create(const std::vector< geo::Vec2f > &points, float z_min, float z_max, ConvexHull &chull, geo::Pose3D &pose)
Segmenter::removeBackground
void removeBackground(cv::Mat &depth_image, const ed::WorldModel &world, const geo::DepthCamera &cam, const geo::Pose3D &sensor_pose, double background_padding)
Definition: src/kinect/segmenter.cpp:64
std::vector< EntityUpdate >
geo::RenderOptions::setBackFaceCulling
void setBackFaceCulling(bool b)
cam
geo::DepthCamera cam
geo::Vec3T
Shape.h
geo::DepthCamera::render
void render(const RenderOptions &opt, RenderResult &res) const
MinMaxRenderer::max_buffer
cv::Mat max_buffer
Definition: src/kinect/segmenter.cpp:128
std::vector::back
T back(T... args)
geo::Transform3T
geo::Shape::getMesh
virtual const Mesh & getMesh() const
queue
geo::Vec2f
Vec2T< float > Vec2f
ed::WorldModel::begin
const_iterator begin() const
ed::WorldModel::const_iterator
EntityIterator const_iterator
ed::EntityConstPtr
shared_ptr< const Entity > EntityConstPtr
ed::WorldModel::end
const_iterator end() const
std::vector::push_back
T push_back(T... args)
image
cv::Mat image
segmenter.h
geo::Transform3T::inverse
Transform3T inverse() const
rgbd::Image
rgbd::View::getRasterizer
const geo::DepthCamera & getRasterizer() const
image.h
geo::RenderResult
geo::RenderOptions
Segmenter::calculatePointsWithin
void calculatePointsWithin(const rgbd::Image &image, const geo::Shape &shape, const geo::Pose3D &shape_pose, cv::Mat &filtered_depth_image) const
Definition: src/kinect/segmenter.cpp:134
geo::Vector3
std::vector::pop_back
T pop_back(T... args)
geo::DepthCamera::project2Dto3D
geo::Vec3T< T > project2Dto3D(int x, int y) const
ed::WorldModel
geo::Vector3::z
const real & z() const
Segmenter::~Segmenter
~Segmenter()
Definition: src/kinect/segmenter.cpp:29
Segmenter::Segmenter
Segmenter()
Definition: src/kinect/segmenter.cpp:23
view.h
world_model.h
MinMaxRenderer
Definition: src/kinect/segmenter.cpp:99
MinMaxRenderer::min_buffer
cv::Mat min_buffer
Definition: src/kinect/segmenter.cpp:127
geo::Vector3::x
const real & x() const
std::queue::push
T push(T... args)
geo::DepthCamera
geo::RenderResult::RenderResult
RenderResult(int width, int height)
std::max
T max(T... args)
entity.h
MinMaxRenderer::renderPixel
void renderPixel(int x, int y, float depth, int)
Definition: src/kinect/segmenter.cpp:110
convex_hull_calc.h
depth_image
cv::Mat depth_image
rgbd::View
geo::Shape
geo::RenderResult::renderPixel
virtual void renderPixel(int x, int y, float depth, int i_triangle)=0