cliff_detector.cpp
Go to the documentation of this file.
00001 /******************************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  * Copyright (c) 2015, Michal Drwiega (drwiega.michal@gmail.com)
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions are met:
00009  *     1. Redistributions of source code must retain the above copyright
00010  *        notice, this list of conditions and the following disclaimer.
00011  *     2. Redistributions in binary form must reproduce the above copyright
00012  *        notice, this list of conditions and the following disclaimer in the
00013  *        documentation and/or other materials provided with the distribution.
00014  *     3. Neither the name of the copyright holder nor the names of its
00015  *        contributors may be used to endorse or promote products derived
00016  *        from this software without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00019  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00020  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00021  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
00022  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00023  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00024  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00025  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00026  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00027  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *****************************************************************************/
00037 #include <cliff_detector/cliff_detector_node.h>
00038 
00039 using namespace cliff_detector;
00040 
00041 //=================================================================================================
00042 // Public methods
00043 //=================================================================================================
00044 CliffDetector::CliffDetector(): depth_sensor_params_update(false) { }
00045 
00046 //=================================================================================================
00047 void CliffDetector::detectCliff( const sensor_msgs::ImageConstPtr& depth_msg,
00048                                            const sensor_msgs::CameraInfoConstPtr& info_msg)
00049 {
00050   // Update data based on depth sensor parameters only if new params values
00051   // or turned on continuous data calculations
00052   if(!depth_sensor_params_update || cam_model_update_)
00053   {
00054     camera_model_.fromCameraInfo(info_msg);
00055 
00056     double angle_min, angle_max, vertical_fov;
00057     double cx = camera_model_.cx(), cy = camera_model_.cy();
00058 
00059     ROS_ASSERT(cx > 0 && cy > 0);
00060 
00061     // Calculate fields of views angles - vertical and horizontal
00062     fieldOfView(angle_min, angle_max, cx, 0, cx, cy, cx, depth_msg->height -1);
00063     vertical_fov = angle_max - angle_min;
00064 
00065     ROS_ASSERT(vertical_fov > 0);
00066 
00067     // Calculate angles between optical axis and rays for each row of image
00068     calcDeltaAngleForImgRows(vertical_fov);
00069 
00070     // Calculate ground distances for every row of depth image
00071     calcGroundDistancesForImgRows(vertical_fov);
00072 
00073     // Sensor tilt compensation
00074     calcTiltCompensationFactorsForImgRows(vertical_fov);
00075 
00076     // Check scan_height vs image_height
00077     if (used_depth_height_ > depth_msg->height)
00078     {
00079       ROS_ERROR("Parameter used_depth_height is higher than height of image.");
00080       used_depth_height_ = depth_msg->height;
00081     }
00082     depth_sensor_params_update = true;
00083   }
00084 
00085   // Check if image encoding is correctly
00086   if (depth_msg->encoding == sensor_msgs::image_encodings::TYPE_16UC1)
00087   {
00088     findCliffInDepthImage(depth_msg);
00089   }
00090   else
00091   {
00092     std::stringstream ss;
00093     ss << "Depth image has unsupported encoding: " << depth_msg->encoding;
00094     throw std::runtime_error(ss.str());
00095   }
00096 }
00097 
00098 //=================================================================================================
00099 void CliffDetector::setRangeLimits( const float rmin, const float rmax )
00100 {
00101   if (rmin >= 0 && rmin < rmax)
00102     range_min_ = rmin;
00103   else
00104   {
00105     range_min_ = 0;
00106     ROS_ERROR("Incorrect value of range minimal parameter. Set default value: 0.");
00107   }
00108   if (rmax >= 0 && rmin < rmax)
00109     range_max_ = rmax;
00110   else
00111   {
00112     range_max_ = 10;
00113     ROS_ERROR("Incorrect value of range maximum parameter. Set default value: 10.");
00114   }
00115 }
00116 
00117 //=================================================================================================
00118 void CliffDetector::setSensorMountHeight (const float height)
00119 {
00120   if( height > 0)
00121     sensor_mount_height_ = height;
00122   else
00123   {
00124     sensor_mount_height_ = 0;
00125     ROS_ERROR("Incorrect value of sensor mount height parameter. Set default value: 0.");
00126   }
00127 }
00128 
00129 //=================================================================================================
00130 void CliffDetector::setSensorTiltAngle (const float angle)
00131 {
00132   if( angle < 90 && angle > -90)
00133     sensor_tilt_angle_  = angle;
00134   else
00135   {
00136     sensor_tilt_angle_  = 0;
00137     ROS_ERROR("Incorrect value of sensor tilt angle parameter. Set default value: 0.");
00138   }
00139 }
00140 
00141 //=================================================================================================
00142 void CliffDetector::setUsedDepthHeight(const unsigned int height)
00143 {
00144   if( height > 0)
00145     used_depth_height_ = height;
00146   else
00147   {
00148     used_depth_height_ = 100;
00149     ROS_ERROR("Incorrect value of used depth height parameter. Set default value: 100.");
00150   }
00151 }
00152 
00153 //=================================================================================================
00154 void CliffDetector::setBlockSize(const int size)
00155 {
00156   if( size > 0 && (size % 2 == 0))
00157     block_size_ = size;
00158   else
00159   {
00160     block_size_ = 8;
00161     ROS_ERROR("Incorrect value of block size. Set default value: 8.");
00162   }
00163 }
00164 
00165 //=================================================================================================
00166 void CliffDetector::setBlockPointsThresh(const int thresh)
00167 {
00168   if( thresh > 0 )
00169     block_points_thresh_ = thresh;
00170   else
00171   {
00172     block_points_thresh_ = 1;
00173     ROS_ERROR("Incorrect value of block points threshold parameter. Set default value: 1.");
00174   }
00175 }
00176 
00177 //=================================================================================================
00178 void CliffDetector::setDepthImgStepRow(const int step)
00179 {
00180   if( step > 0 )
00181     depth_image_step_row_ = step;
00182   else
00183   {
00184     depth_image_step_row_ = 1;
00185     ROS_ERROR("Incorrect value depth image row step parameter. Set default value: 1.");
00186   }
00187 }
00188 
00189 //=================================================================================================
00190 void CliffDetector::setDepthImgStepCol(const int step)
00191 {
00192   if( step > 0 )
00193     depth_image_step_col_ = step;
00194   else
00195   {
00196     depth_image_step_col_ = 1;
00197     ROS_ERROR("Incorrect value depth image column step parameter. Set default value: 1.");
00198   }
00199 }
00200 
00201 //=================================================================================================
00202 void CliffDetector::setGroundMargin (const float margin)
00203 {
00204   if( margin > 0)
00205     ground_margin_ = margin;
00206   else
00207   {
00208     ground_margin_ = 0;
00209     ROS_ERROR("Incorrect value of ground margin parameter. Set default value: 0.");
00210   }
00211 }
00212 
00213 //=================================================================================================
00214 // Private methods
00215 //=================================================================================================
00216 double CliffDetector::lengthOfVector(const cv::Point3d& vec) const
00217 {
00218   return sqrt(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z);
00219 }
00220 
00221 //=================================================================================================
00222 double CliffDetector::angleBetweenRays(const cv::Point3d& ray1, const cv::Point3d& ray2) const
00223 {
00224   double dot = ray1.x*ray2.x + ray1.y*ray2.y + ray1.z*ray2.z;
00225 
00226   return acos(dot / (lengthOfVector(ray1) * lengthOfVector(ray2)));
00227 }
00228 
00229 //=================================================================================================
00230 void CliffDetector::fieldOfView(double & min, double & max, double x1, double y1,
00231                                      double xc, double yc, double x2, double y2)
00232 {
00233   cv::Point2d raw_pixel_left(x1, y1);
00234   cv::Point2d rect_pixel_left = camera_model_.rectifyPoint(raw_pixel_left);
00235   cv::Point3d left_ray = camera_model_.projectPixelTo3dRay(rect_pixel_left);
00236 
00237   cv::Point2d raw_pixel_right(x2, y2);
00238   cv::Point2d rect_pixel_right = camera_model_.rectifyPoint(raw_pixel_right);
00239   cv::Point3d right_ray = camera_model_.projectPixelTo3dRay(rect_pixel_right);
00240 
00241   cv::Point2d raw_pixel_center(xc, yc);
00242   cv::Point2d rect_pixel_center = camera_model_.rectifyPoint(raw_pixel_center);
00243   cv::Point3d center_ray = camera_model_.projectPixelTo3dRay(rect_pixel_center);
00244 
00245   min = -angleBetweenRays(center_ray, right_ray);
00246   max = angleBetweenRays(left_ray, center_ray);
00247 
00248   ROS_ASSERT(min < 0 && max > 0);
00249 }
00250 
00251 
00252 //=================================================================================================
00253 void CliffDetector::calcDeltaAngleForImgRows(double vertical_fov)
00254 {
00255   const unsigned int img_height = camera_model_.fullResolution().height;
00256 
00257   delta_row_.resize(img_height);
00258 
00259   // Angle between ray and optical center
00260   for(unsigned int i = 0; i < img_height; i++)
00261     delta_row_[i] = vertical_fov * (i - camera_model_.cy() - 0.5) / ((double)img_height - 1);
00262 }
00263 
00264 //=================================================================================================
00265 void CliffDetector::calcGroundDistancesForImgRows(double vertical_fov)
00266 {
00267   const double alpha = sensor_tilt_angle_ * M_PI / 180.0; // Sensor tilt angle in radians
00268   const unsigned int img_height = camera_model_.fullResolution().height;
00269 
00270   ROS_ASSERT(img_height >= 0);
00271 
00272   dist_to_ground_.resize(img_height);
00273 
00274   // Calculations for each row of image
00275   for(unsigned int i = 0; i < img_height; i++)
00276   {
00277     // Angle between ray and optical center
00278     //double delta = vertical_fov * (i - camera_model_.cy() - 0.5) / ((double)img_height - 1);
00279 
00280     if ((delta_row_[i] + alpha) > 0)
00281     {
00282       dist_to_ground_[i] = sensor_mount_height_ * sin(M_PI/2 - delta_row_[i]) * 1000
00283           / cos(M_PI/2 - delta_row_[i] - alpha);
00284       ROS_ASSERT(dist_to_ground_[i] > 0);
00285     }
00286     else
00287       dist_to_ground_[i] = 100 * 1000;
00288   }
00289 }
00290 
00291 //=================================================================================================
00292 void CliffDetector::calcTiltCompensationFactorsForImgRows(double vertical_fov)
00293 {
00294   const double alpha = sensor_tilt_angle_ * M_PI / 180.0; // Sensor tilt angle in radians
00295   const unsigned int img_height = camera_model_.fullResolution().height;
00296 
00297   ROS_ASSERT(img_height >= 0);
00298 
00299   tilt_compensation_factor_.resize(img_height);
00300 
00301   for(unsigned int i = 0; i < img_height; i++) // Process all rows
00302   {
00303 //    double delta = vertical_fov * (i - camera_model_.cy() - 0.5) / ((double)img_height - 1);
00304 
00305     tilt_compensation_factor_[i] = sin(M_PI/2 - delta_row_[i] - alpha)
00306                                     / sin(M_PI/2 - delta_row_[i]);
00307     ROS_ASSERT(tilt_compensation_factor_[i] > 0 && tilt_compensation_factor_[i] < 10);
00308   }
00309 }
00310 
00311 //=================================================================================================
00312 void CliffDetector::findCliffInDepthImage( const sensor_msgs::ImageConstPtr &depth_msg)
00313 {
00314   enum point { Row, Col, Depth };
00315 
00316   const uint16_t* depth_row = reinterpret_cast<const uint16_t*>(&depth_msg->data[0]);
00317 
00318   const unsigned int row_size = depth_msg->step / sizeof(uint16_t);
00319 
00320   const unsigned int img_height = camera_model_.fullResolution().height;
00321   const unsigned int img_width = camera_model_.fullResolution().width;
00322 
00323   if ( (block_size_ % 2) !=0 )
00324   {
00325     ROS_ERROR("Block size should be even number. Value will be decreased by one.");
00326     block_size_--;
00327   }
00328 
00329   const unsigned int block_cols_nr = img_width / block_size_;
00330   const unsigned int block_rows_nr = used_depth_height_ / block_size_;
00331 
00332   const int ground_margin_mm = ground_margin_ * 1000;
00333   const unsigned int range_min_mm = range_min_ * 1000;
00334   const unsigned int range_max_mm = range_max_ * 1000;
00335 
00336   // Check if points thresh isn't too big
00337   if (block_points_thresh_ >= (block_size_ * block_size_ / depth_image_step_col_
00338                                / depth_image_step_row_))
00339   {
00340     ROS_ERROR("Block points threshold is too big. Maximum admissible value will be set.");
00341     block_points_thresh_ = block_size_*block_size_ / depth_image_step_col_ / depth_image_step_row_;
00342   }
00343 
00344   // Vector for block, constains rows, cols, depth values
00345   //  std::vector<std::array<int, 3> > tpoints;
00346   std::vector<std::vector<int> >tpoints (block_size_ * block_size_, std::vector<int>(3));
00347 
00348   // Rows, cols and depth values for points which apply to stairs
00349   // std::vector<std::array<int, 3> > stairs_points;
00350   std::vector<std::vector<int> > stairs_points;
00351 
00352 
00353 
00354   // Indicates which point from tpoints vector corresponds to which pixel in block
00355   std::vector<int> px_nr;
00356 
00357   // Resize to size of block
00358   //tpoints.resize(block_size_ * block_size_);
00359   px_nr.resize(block_size_ * block_size_);
00360 
00361   // Four pixels in center of block
00362   const unsigned int c = block_size_ / 2;
00363   std::vector<unsigned int> center_points(4);
00364   center_points[0] = c * block_size_ + c-1;
00365   center_points[1] = c * block_size_ + c;
00366   center_points[2] = (c-1) * block_size_ + c-1;
00367   center_points[3] = (c-1)*block_size_ + c ;
00368 
00369   // Loop over each block row in image, bj - block column
00370   for (unsigned int bj = 0; bj < block_rows_nr; ++bj)
00371   {
00372     // Loop over each block column in image, bi - block row
00373     for (unsigned int bi = 0; bi < block_cols_nr; ++bi)
00374     {
00375       // Start of block processing
00376       //-------------------------------------------------------------------------------
00377       unsigned int block_cnt = 0;
00378       std::fill(px_nr.begin(), px_nr.end(), -1);
00379 
00380       // Loop over each row in block, j - column
00381       for (unsigned int j = 0; j < block_size_; j += depth_image_step_row_)
00382       {
00383         // Loop over each column in block, i - row
00384         for (unsigned int i = 0; i < block_size_; i += depth_image_step_col_)
00385         {
00386           // Rows from bottom of image
00387           unsigned int row = (img_height - 1 ) - ( bj * block_size_+ j );
00388           unsigned int col = bi * block_size_ + i;
00389           ROS_ASSERT(row < img_height && col < img_width);
00390 
00391           unsigned int d = depth_row[row_size * row + col];
00392 
00393           // Check if distance to point is greater than distance to ground plane
00394           if (d > (dist_to_ground_[row] + ground_margin_mm) &&
00395               d > range_min_mm && d < range_max_mm)
00396           {
00397             tpoints[block_cnt][Row] = row;
00398             tpoints[block_cnt][Col] = col;
00399             tpoints[block_cnt][Depth] = d;
00400             px_nr[j * block_size_ + i] = block_cnt;
00401             block_cnt++;
00402           }
00403         }
00404       } // Finished block checking
00405       // Check if number of stairs points in block exceeded threshold value
00406       if (block_cnt >= block_points_thresh_)
00407       {
00408         // Block size is even. So first we check four pixels in center of block.
00409         if (px_nr[center_points[0]] > 0)
00410           stairs_points.push_back(tpoints[px_nr[center_points[0]]]);
00411         else if (px_nr[center_points[1]] > 0)
00412           stairs_points.push_back(tpoints[px_nr[center_points[1]]]);
00413         else if (px_nr[center_points[2]] > 0)
00414           stairs_points.push_back(tpoints[px_nr[center_points[2]]]);
00415         else if (px_nr[center_points[3]] > 0)
00416           stairs_points.push_back(tpoints[px_nr[center_points[3]]]);
00417         else
00418         { // Otherwise add all points from block to stairs points vector
00419           stairs_points.insert(stairs_points.end(), tpoints.begin(), tpoints.begin() + block_cnt);
00420         }
00421       }
00422       block_cnt = 0;
00423       //-------------------------------------------------------------------------------
00424     }
00425   }
00426 
00427   // In this section points from vector are added to set which will be published and
00428   // it is created new, modificated depth image
00429 
00430   std::vector<std::vector<int> >::iterator it;
00431   geometry_msgs::Point32 pt;
00432 
00433   if (publish_depth_enable_)
00434     new_depth_msg_ = *depth_msg;
00435 
00436   uint16_t* new_depth_row = reinterpret_cast<uint16_t*>(&new_depth_msg_.data[0]);
00437 
00438   // Set header and size of points list in message
00439   stairs_points_msg_.header = depth_msg->header;
00440   stairs_points_msg_.size = (unsigned int) stairs_points.size();
00441   stairs_points_msg_.points.clear();
00442   pt.y = 0;
00443 
00444   for(it = stairs_points.begin(); it != stairs_points.end(); ++it)
00445   {
00446     // Calculate point in XZ plane -- depth (z)
00447     //pt.z = (float)(*it)[Depth] * tilt_compensation_factor_[(*it)[Row]] / 1000.0f;
00448     unsigned int row = (*it)[Row];
00449     pt.z = sensor_mount_height_ / std::tan(sensor_tilt_angle_ * M_PI / 180.0 + delta_row_[row]);
00450 
00451     // Calculate x value
00452     // pt.x = ((*it)[Col] - camera_model_.cx()) * (*it)[Depth] / camera_model_.fx() / 1000.0f;
00453     double depth = sensor_mount_height_ / std::sin(sensor_tilt_angle_*M_PI/180.0 + delta_row_[row]);
00454 
00455     pt.x = ((*it)[Col] - camera_model_.cx()) * depth / camera_model_.fx();
00456 
00457     // Add point to message
00458     stairs_points_msg_.points.push_back(pt);
00459 
00460     if (publish_depth_enable_)
00461     {
00462       ROS_ASSERT(row_size * (*it)[Row] + (*it)[Col] < (new_depth_msg_.height * new_depth_msg_.width));
00463       new_depth_row[row_size * (*it)[Row] + (*it)[Col]] = 10000U;
00464     }
00465   }
00466   ROS_DEBUG_STREAM("Stairs points: " << stairs_points.size());
00467 }
00468 
00469 //=================================================================================================
00470 //=================================================================================================


cliff_detector
Author(s): Michal Drwiega
autogenerated on Thu Jun 6 2019 22:10:46