cliff_detector.cpp
Go to the documentation of this file.
2 
3 namespace cliff_detector {
4 
5 CliffDetector::CliffDetector(): depth_sensor_params_update(false) {}
6 
7 void CliffDetector::detectCliff(const sensor_msgs::ImageConstPtr& depth_msg,
8  const sensor_msgs::CameraInfoConstPtr& info_msg) {
9  // Update data based on depth sensor parameters only if new params values
10  // or turned on continuous data calculations
12  {
13  camera_model_.fromCameraInfo(info_msg);
14 
15  double angle_min, angle_max, vertical_fov;
16  double cx = camera_model_.cx(), cy = camera_model_.cy();
17 
18  ROS_ASSERT(cx > 0 && cy > 0);
19 
20  // Calculate fields of views angles - vertical and horizontal
21  fieldOfView(angle_min, angle_max, cx, 0, cx, cy, cx, depth_msg->height -1);
22  vertical_fov = angle_max - angle_min;
23 
24  ROS_ASSERT(vertical_fov > 0);
25 
26  // Calculate angles between optical axis and rays for each row of image
27  calcDeltaAngleForImgRows(vertical_fov);
28 
29  // Calculate ground distances for every row of depth image
30  calcGroundDistancesForImgRows(vertical_fov);
31 
32  // Sensor tilt compensation
34 
35  // Check scan_height vs image_height
36  if (used_depth_height_ > depth_msg->height) {
37  ROS_ERROR("Parameter used_depth_height is higher than height of image.");
38  used_depth_height_ = depth_msg->height;
39  }
41  }
42 
43  // Check the image encoding
44  if (depth_msg->encoding == sensor_msgs::image_encodings::TYPE_16UC1) {
45  findCliffInDepthImage<uint16_t>(depth_msg);
46  }
47  else if (depth_msg->encoding == sensor_msgs::image_encodings::TYPE_32FC1) {
48  findCliffInDepthImage<float>(depth_msg);
49  }
50  else {
51  std::stringstream ss;
52  ss << "Depth image has unsupported encoding: " << depth_msg->encoding;
53  throw std::runtime_error(ss.str());
54  }
55 }
56 
57 void CliffDetector::setRangeLimits( const float rmin, const float rmax) {
58  if (rmin >= 0 && rmin < rmax) {
59  range_min_ = rmin;
60  }
61  else {
62  range_min_ = 0;
63  ROS_ERROR("Incorrect value of range minimal parameter. Set default value: 0.");
64  }
65  if (rmax >= 0 && rmin < rmax) {
66  range_max_ = rmax;
67  }
68  else {
69  range_max_ = 10;
70  ROS_ERROR("Incorrect value of range maximum parameter. Set default value: 10.");
71  }
72 }
73 
74 void CliffDetector::setSensorMountHeight (const float height) {
75  if (height > 0) {
76  sensor_mount_height_ = height;
77  }
78  else {
80  ROS_ERROR("Incorrect value of sensor mount height parameter. Set default value: 0.");
81  }
82 }
83 
84 void CliffDetector::setSensorTiltAngle (const float angle) {
85  if (angle < 90 && angle > -90) {
86  sensor_tilt_angle_ = angle;
87  }
88  else {
90  ROS_ERROR("Incorrect value of sensor tilt angle parameter. Set default value: 0.");
91  }
92 }
93 
94 void CliffDetector::setUsedDepthHeight(const unsigned height) {
95  if (height > 0) {
96  used_depth_height_ = height;
97  }
98  else {
99  used_depth_height_ = 100;
100  ROS_ERROR("Incorrect value of used depth height parameter. Set default value: 100.");
101  }
102 }
103 
104 void CliffDetector::setBlockSize(const int size) {
105  if (size > 0 && (size % 2 == 0)) {
106  block_size_ = size;
107  }
108  else {
109  block_size_ = 8;
110  ROS_ERROR("Incorrect value of block size. Set default value: 8.");
111  }
112 }
113 
114 void CliffDetector::setBlockPointsThresh(const int thresh) {
115  if (thresh > 0) {
116  block_points_thresh_ = thresh;
117  }
118  else {
120  ROS_ERROR("Incorrect value of block points threshold parameter. Set default value: 1.");
121  }
122 }
123 
124 void CliffDetector::setDepthImgStepRow(const int step) {
125  if (step > 0) {
126  depth_image_step_row_ = step;
127  }
128  else {
130  ROS_ERROR("Incorrect value depth image row step parameter. Set default value: 1.");
131  }
132 }
133 
134 void CliffDetector::setDepthImgStepCol(const int step) {
135  if (step > 0 ) {
136  depth_image_step_col_ = step;
137  }
138  else {
140  ROS_ERROR("Incorrect value depth image column step parameter. Set default value: 1.");
141  }
142 }
143 
144 void CliffDetector::setGroundMargin (const float margin) {
145  if (margin > 0) {
146  ground_margin_ = margin;
147  }
148  else {
149  ground_margin_ = 0;
150  ROS_ERROR("Incorrect value of ground margin parameter. Set default value: 0.");
151  }
152 }
153 
154 double CliffDetector::lengthOfVector(const cv::Point3d& vec) const {
155  return sqrt(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z);
156 }
157 
158 double CliffDetector::angleBetweenRays(const cv::Point3d& ray1, const cv::Point3d& ray2) const {
159  double dot = ray1.x*ray2.x + ray1.y*ray2.y + ray1.z*ray2.z;
160  return acos(dot / (lengthOfVector(ray1) * lengthOfVector(ray2)));
161 }
162 
163 void CliffDetector::fieldOfView(double & min, double & max, double x1, double y1,
164  double xc, double yc, double x2, double y2) {
165  cv::Point2d raw_pixel_left(x1, y1);
166  cv::Point2d rect_pixel_left = camera_model_.rectifyPoint(raw_pixel_left);
167  cv::Point3d left_ray = camera_model_.projectPixelTo3dRay(rect_pixel_left);
168 
169  cv::Point2d raw_pixel_right(x2, y2);
170  cv::Point2d rect_pixel_right = camera_model_.rectifyPoint(raw_pixel_right);
171  cv::Point3d right_ray = camera_model_.projectPixelTo3dRay(rect_pixel_right);
172 
173  cv::Point2d raw_pixel_center(xc, yc);
174  cv::Point2d rect_pixel_center = camera_model_.rectifyPoint(raw_pixel_center);
175  cv::Point3d center_ray = camera_model_.projectPixelTo3dRay(rect_pixel_center);
176 
177  min = -angleBetweenRays(center_ray, right_ray);
178  max = angleBetweenRays(left_ray, center_ray);
179 
180  ROS_ASSERT(min < 0 && max > 0);
181 }
182 
183 void CliffDetector::calcDeltaAngleForImgRows(double vertical_fov) {
184  const unsigned img_height = camera_model_.fullResolution().height;
185 
186  delta_row_.resize(img_height);
187 
188  // Angle between ray and optical center
189  for(unsigned i = 0; i < img_height; i++) {
190  delta_row_[i] = vertical_fov * (i - camera_model_.cy() - 0.5) / ((double)img_height - 1);
191  }
192 }
193 
194 void CliffDetector::calcGroundDistancesForImgRows([[maybe_unused]] double vertical_fov) {
195  const double alpha = sensor_tilt_angle_ * M_PI / 180.0; // Sensor tilt angle in radians
196  const unsigned img_height = camera_model_.fullResolution().height;
197 
198  ROS_ASSERT(img_height >= 0);
199 
200  dist_to_ground_.resize(img_height);
201 
202  // Calculations for each row of image
203  for (unsigned i = 0; i < img_height; i++) {
204  // Angle between ray and optical center
205  if ((delta_row_[i] + alpha) > 0) {
206  dist_to_ground_[i] = sensor_mount_height_ * sin(M_PI/2 - delta_row_[i])
207  / cos(M_PI/2 - delta_row_[i] - alpha);
208  ROS_ASSERT(dist_to_ground_[i] > 0);
209  }
210  else {
211  dist_to_ground_[i] = 100.0;
212  }
213  }
214 }
215 
217 {
218  const double alpha = sensor_tilt_angle_ * M_PI / 180.0; // Sensor tilt angle in radians
219  const unsigned img_height = camera_model_.fullResolution().height;
220 
221  ROS_ASSERT(img_height >= 0);
222 
223  tilt_compensation_factor_.resize(img_height);
224 
225  for (unsigned i = 0; i < img_height; i++) { // Process all rows
226  tilt_compensation_factor_[i] = sin(M_PI/2 - delta_row_[i] - alpha)
227  / sin(M_PI/2 - delta_row_[i]);
229  }
230 }
231 
232 template<typename T>
233 void CliffDetector::findCliffInDepthImage(const sensor_msgs::ImageConstPtr &depth_msg)
234 {
235  enum Point { Row, Col, Depth };
236 
237  const T* data = reinterpret_cast<const T*>(&depth_msg->data[0]);
238  const unsigned row_size = depth_msg->width;
239  const unsigned img_height = camera_model_.fullResolution().height;
240  const unsigned img_width = camera_model_.fullResolution().width;
241 
242  if ((block_size_ % 2) != 0) {
243  ROS_ERROR("Block size should be even number. Value will be decreased by one.");
244  block_size_--;
245  }
246 
247  const unsigned block_cols_nr = img_width / block_size_;
248  const unsigned block_rows_nr = used_depth_height_ / block_size_;
249 
250  // Check if points thresh isn't too big
253  ROS_ERROR("Block points threshold is too big. Maximum admissible value will be set.");
255  }
256 
257  // Vector for block, constains rows, cols, depth values
258  std::vector<std::vector<int> >tpoints (block_size_ * block_size_, std::vector<int>(3));
259 
260  // Rows, cols and depth values for points which apply to stairs
261  std::vector<std::vector<int> > stairs_points;
262 
263  // Indicates which point from tpoints vector corresponds to which pixel in block
264  std::vector<int> px_nr;
265  px_nr.resize(block_size_ * block_size_);
266 
267  // Four pixels in center of block
268  const unsigned c = block_size_ / 2;
269  std::vector<unsigned> center_points(4);
270  center_points[0] = c * block_size_ + c-1;
271  center_points[1] = c * block_size_ + c;
272  center_points[2] = (c-1) * block_size_ + c-1;
273  center_points[3] = (c-1)*block_size_ + c ;
274 
275  // Loop over each block row in image, bj - block column
276  for (unsigned bj = 0; bj < block_rows_nr; ++bj) {
277  // Loop over each block column in image, bi - block row
278  for (unsigned bi = 0; bi < block_cols_nr; ++bi) {
279 
280  // Block processing
281  unsigned block_cnt = 0;
282  std::fill(px_nr.begin(), px_nr.end(), -1);
283 
284  // Loop over each row in block, j - column
285  for (unsigned j = 0; j < block_size_; j += depth_image_step_row_) {
286  // Loop over each column in block, i - row
287  for (unsigned i = 0; i < block_size_; i += depth_image_step_col_) {
288  // Rows from bottom of image
289  unsigned row = (img_height - 1 ) - (bj * block_size_+ j);
290  unsigned col = bi * block_size_ + i;
291  ROS_ASSERT(row < img_height && col < img_width);
292 
293  float d = 0.0;
294 
295  if (typeid(T) == typeid(uint16_t)) {
296  unsigned depth_raw_mm = static_cast<unsigned>(data[row_size * row + col]);
297  d = static_cast<float>(depth_raw_mm) / 1000.0;
298  }
299  else if (typeid(T) == typeid(float)) {
300  d = static_cast<float>(data[row_size * row + col]);
301  }
302 
303  // Check if distance to point is greater than distance to ground plane
304  if (d > (dist_to_ground_[row] + ground_margin_) && d > range_min_ && d < range_max_) {
305  tpoints[block_cnt][Row] = row;
306  tpoints[block_cnt][Col] = col;
307  tpoints[block_cnt][Depth] = d;
308  px_nr[j * block_size_ + i] = block_cnt;
309  block_cnt++;
310  }
311  }
312  }
313 
314  // Check if number of stairs points in block exceeded threshold value
315  if (block_cnt >= block_points_thresh_) {
316  // Block size is even. So first we check four pixels in center of block.
317  if (px_nr[center_points[0]] > 0) {
318  stairs_points.push_back(tpoints[px_nr[center_points[0]]]);
319  }
320  else if (px_nr[center_points[1]] > 0) {
321  stairs_points.push_back(tpoints[px_nr[center_points[1]]]);
322  }
323  else if (px_nr[center_points[2]] > 0) {
324  stairs_points.push_back(tpoints[px_nr[center_points[2]]]);
325  }
326  else if (px_nr[center_points[3]] > 0) {
327  stairs_points.push_back(tpoints[px_nr[center_points[3]]]);
328  }
329  else { // Otherwise add all points from block to stairs points vector
330  stairs_points.insert(stairs_points.end(), tpoints.begin(), tpoints.begin() + block_cnt);
331  }
332  }
333  }
334  }
335 
336  std::vector<std::vector<int> >::iterator it;
337  geometry_msgs::Point32 pt;
338 
339  if (publish_depth_enable_) {
340  new_depth_msg_ = *depth_msg;
341  }
342 
343  T* new_depth_row = reinterpret_cast<T*>(&new_depth_msg_.data[0]);
344 
345  // Set header and size of points list in message
346  stairs_points_msg_.header = depth_msg->header;
347  stairs_points_msg_.size = (unsigned) stairs_points.size();
348  stairs_points_msg_.points.clear();
349  pt.y = 0;
350 
351  for (it = stairs_points.begin(); it != stairs_points.end(); ++it) {
352  // Calculate point in XZ plane -- depth (z)
353  unsigned row = (*it)[Row];
354  pt.z = sensor_mount_height_ / std::tan(sensor_tilt_angle_ * M_PI / 180.0 + delta_row_[row]);
355 
356  // Calculate x value
357  double depth = sensor_mount_height_ / std::sin(sensor_tilt_angle_*M_PI/180.0 + delta_row_[row]);
358 
359  pt.x = ((*it)[Col] - camera_model_.cx()) * depth / camera_model_.fx();
360 
361  // Add point to message
362  stairs_points_msg_.points.push_back(pt);
363 
365  {
366  ROS_ASSERT(row_size * (*it)[Row] + (*it)[Col] < (new_depth_msg_.height * new_depth_msg_.width));
367  new_depth_row[row_size * (*it)[Row] + (*it)[Col]] = 10000U;
368  }
369  }
370  ROS_DEBUG_STREAM("Stairs points: " << stairs_points.size());
371 }
372 
373 template void CliffDetector::findCliffInDepthImage<uint16_t>(const sensor_msgs::ImageConstPtr&);
374 template void CliffDetector::findCliffInDepthImage<float>(const sensor_msgs::ImageConstPtr&);
375 
376 } // namespace cliff_detector
d
void setDepthImgStepCol(const int step)
setDepthImgStepCol
void detectCliff(const sensor_msgs::ImageConstPtr &depth_msg, const sensor_msgs::CameraInfoConstPtr &info_msg)
detectCliff detects descending stairs based on the information in a depth image
bool cam_model_update_
Determines if continuously cam model update required.
float range_min_
Stores the current minimum range to use.
depth_nav_msgs::Point32List stairs_points_msg_
sensor_msgs::Image new_depth_msg_
cv::Point3d projectPixelTo3dRay(const cv::Point2d &uv_rect) const
void setDepthImgStepRow(const int step)
setDepthImgStepRow
float range_max_
Stores the current maximum range to use.
float ground_margin_
Margin for ground points feature detector (m)
void setUsedDepthHeight(const unsigned int height)
setUsedDepthHeight
image_geometry::PinholeCameraModel camera_model_
Class for managing sensor_msgs/CameraInfo messages.
unsigned int used_depth_height_
Used depth height from img bottom (px)
unsigned int block_points_thresh_
Threshold value of points in block to admit stairs.
void findCliffInDepthImage(const sensor_msgs::ImageConstPtr &depth_msg)
unsigned int block_size_
Square block (subimage) size (px).
void setSensorMountHeight(const float height)
setSensorMountHeight sets the height of sensor mount (in meters) from ground
bool fromCameraInfo(const sensor_msgs::CameraInfo &msg)
void setBlockSize(const int size)
setBlockSize sets size of square block (subimage) used in cliff detector
const std::string TYPE_32FC1
std::vector< unsigned int > dist_to_ground_
Calculated distances to ground for every row of depth image in mm.
float sensor_mount_height_
Height of sensor mount from ground.
const std::string TYPE_16UC1
bool publish_depth_enable_
Determines if depth should be republished.
void calcGroundDistancesForImgRows(double vertical_fov)
Calculates distances to ground for every row of depth image.
unsigned int depth_image_step_col_
Columns step in depth processing (px).
cv::Point2d rectifyPoint(const cv::Point2d &uv_raw) const
#define ROS_DEBUG_STREAM(args)
float sensor_tilt_angle_
Sensor tilt angle (degrees)
void fieldOfView(double &min, double &max, double x1, double y1, double xc, double yc, double x2, double y2)
unsigned int depth_image_step_row_
Rows step in depth processing (px).
void calcDeltaAngleForImgRows(double vertical_fov)
calcDeltaAngleForImgRows
void calcTiltCompensationFactorsForImgRows()
calcTiltCompensationFactorsForImgRows calculate factors used in tilt compensation ...
double angleBetweenRays(const cv::Point3d &ray1, const cv::Point3d &ray2) const
void setGroundMargin(const float margin)
setGroundMargin sets the floor margin (in meters)
void setBlockPointsThresh(const int thresh)
setBlockPointsThresh sets threshold value of pixels in block to set block as stairs ...
double lengthOfVector(const cv::Point3d &vec) const
lengthOfVector calculates length of 3D vector.
#define ROS_ASSERT(cond)
std::vector< double > tilt_compensation_factor_
Calculated sensor tilt compensation factors.
#define ROS_ERROR(...)
void setSensorTiltAngle(const float angle)
setSensorTiltAngle sets the sensor tilt angle (in degrees)
std::vector< double > delta_row_
void setRangeLimits(const float rmin, const float rmax)
setRangeLimits sets the minimum and maximum range of depth value from RGBD sensor.


cliff_detector
Author(s): Michal Drwiega (http://www.mdrwiega.com)
autogenerated on Wed May 5 2021 02:56:04