LCOV - code coverage report
Current view: top level - src/PointCloud - ObjectInterpreter.cpp (source / functions) Hit Total Coverage
Test: HLR Lines: 157 158 99.4 %
Date: 2018-01-24 13:33:16 Functions: 17 17 100.0 %

          Line data    Source code
       1             : #include "ObjectInterpreter.hpp"
       2             : #include <iterator>
       3             : #include <utility>
       4             : #include <boost/filesystem.hpp>
       5             : #include <boost/filesystem/fstream.hpp>
       6             : #include <boost/make_shared.hpp>
       7             : #include <boost/range/algorithm/find_if.hpp>
       8             : #include <boost/range/algorithm/sort.hpp>
       9             : #include <boost/range/algorithm/transform.hpp>
      10             : #include <CGAL/convex_hull_2.h>
      11             : #include <pcl/PointIndices.h>
      12             : #include <pcl/io/pcd_io.h>
      13             : #include <pcl/octree/octree.h>
      14             : #include <pcl/octree/octree_impl.h>
      15             : #include <pcl/octree/octree_search.h>
      16             : #include <pcl/point_types.h>
      17             : #include <pcl/search/kdtree.h>
      18             : #include <pcl/segmentation/extract_clusters.h>
      19             : #include <pcl/visualization/cloud_viewer.h>
      20             : 
      21             : namespace HLR
      22             : {
      23             : namespace PointCloud
      24             : {
      25             : namespace
      26             : {
      27          18 : long get_milliseconds_since_epoch()
      28             : {
      29          36 :     return std::chrono::duration_cast<std::chrono::milliseconds>(
      30          36 :                std::chrono::time_point_cast<std::chrono::milliseconds>(
      31          36 :                    std::chrono::system_clock::now())
      32          36 :                    .time_since_epoch())
      33          36 :         .count();
      34             : }
      35             : }
      36             : 
      37          16 : ObjectInterpreter::ObjectInterpreter()
      38          16 :     : last_measure_time(get_milliseconds_since_epoch())
      39          16 : {}
      40             : 
      41           4 : std::vector<Object> ObjectInterpreter::determine_speed(
      42             :     std::vector<Object>& objects,
      43             :     long milliseconds_since_epoch /*= -1*/)
      44             : {
      45           4 :     if (milliseconds_since_epoch == -1)
      46             :     {
      47           2 :         milliseconds_since_epoch = get_milliseconds_since_epoch();
      48             :     }
      49             : 
      50           4 :     if (last_measure_time > 0)
      51             :     {
      52             :         // std::find_if(objects.begin(), objects.end(), [](const auto& o){return
      53             :         // !o.is_deleted );
      54             : 
      55          10 :         for (Object& current_object : objects)
      56             :         {
      57           6 :             if (!current_object.is_deleted)
      58             :             {
      59             :                 const auto same_object_it{boost::range::find_if(
      60           4 :                     history.back(),
      61          12 :                     [&current_object](const auto& history_object) {
      62          12 :                         return history_object.id == current_object.id;
      63          14 :                     })};
      64             : 
      65           4 :                 if (same_object_it != history.back().end())
      66             :                 {
      67             :                     // gets the miliseconds since epoch
      68           4 :                     double time_difference_ms =
      69           4 :                         static_cast<double>(milliseconds_since_epoch)
      70           4 :                         - static_cast<double>(last_measure_time);
      71           4 :                     double time_difference =
      72             :                         time_difference_ms / static_cast<double>(1000);
      73             : 
      74           4 :                     double x_difference = current_object.middlepoint.x()
      75           4 :                                           - same_object_it->middlepoint.x();
      76           4 :                     double y_difference = current_object.middlepoint.y()
      77           4 :                                           - same_object_it->middlepoint.y();
      78           4 :                     double z_difference = current_object.middlepoint.z()
      79           4 :                                           - same_object_it->middlepoint.z();
      80           4 :                     current_object.speed =
      81           8 :                         Object::Vector(x_difference / time_difference,
      82           8 :                                        y_difference / time_difference,
      83          12 :                                        z_difference / time_difference);
      84             :                 }
      85             :             }
      86             :         }
      87             :     }
      88           4 :     last_measure_time = milliseconds_since_epoch;
      89           4 :     return objects;
      90             : }
      91             : 
      92           2 : std::vector<Object> ObjectInterpreter::get_current_objects(
      93             :     const HLR::Vision::CupDetection& detected_cups,
      94             :     const pcl::PointCloud<pcl::PointXYZ>& point_cloud)
      95             : {
      96           2 :     merge_objects(detected_cups, point_cloud);
      97           2 :     return current_objects;
      98             : }
      99             : 
     100           7 : std::vector<Object> ObjectInterpreter::get_changed_objects(
     101             :     const HLR::Vision::CupDetection& detected_cups,
     102             :     const pcl::PointCloud<pcl::PointXYZ>& point_cloud)
     103             : {
     104           7 :     merge_objects(detected_cups, point_cloud);
     105           7 :     std::vector<Object> changed_objects;
     106             : 
     107           7 :     for (const auto& i : current_objects)
     108             :     {
     109           0 :         add_only_changed_objects(i, changed_objects);
     110             :     }
     111             : 
     112           7 :     return changed_objects;
     113             : }
     114             : 
     115           2 : void ObjectInterpreter::add_only_changed_objects(
     116             :     const Object& object,
     117             :     std::vector<Object>& changed_objects) const
     118             : {
     119           2 :     bool is_new = true;
     120           6 :     for (const auto& i : history.front())
     121             :     {
     122           4 :         if (object.id == i.id)
     123             :         {
     124           2 :             is_new = false;
     125           2 :             if (object != i)
     126             :             {
     127           2 :                 changed_objects.push_back(object);
     128             :             }
     129             :         }
     130             :     }
     131           2 :     if (is_new)
     132             :     {
     133           1 :         changed_objects.push_back(object);
     134             :     }
     135           2 : }
     136             : 
     137           9 : void ObjectInterpreter::merge_objects(
     138             :     const HLR::Vision::CupDetection& detected_cups,
     139             :     const pcl::PointCloud<pcl::PointXYZ>& point_cloud)
     140             : {
     141           9 :     history.push_front(std::move(current_objects));
     142           9 :     current_objects = lidar_interpreter.get_objects(detected_cups, point_cloud);
     143           9 : }
     144             : 
     145           3 : std::vector<Object> ObjectInterpreter::isolate_objects(
     146             :     const pcl::PointCloud<pcl::PointXYZ>& newest_cloud)
     147             : {
     148           3 :     constexpr double height = 3.0;
     149             : 
     150             :     // Empty Object vector which is going to contain all objects.
     151             :     // Will be returned at the end of the function.
     152           3 :     std::vector<Object> found_objects;
     153             : 
     154             :     // Check if input contains data to prevent program from crashing.
     155           3 :     if (!newest_cloud.empty())
     156             :     {
     157             :         // This function needs a boost shared_pointer to function.
     158             :         auto cloud_pointer =
     159             :             boost::make_shared<const pcl::PointCloud<pcl::PointXYZ>>(
     160           6 :                 newest_cloud);
     161             :         pcl::PointCloud<pcl::PointXYZ>::Ptr cluster_pointer(
     162           6 :             new pcl::PointCloud<pcl::PointXYZ>);
     163             : 
     164             :         // Variables and setup for search algorithm for Euclidian clustering.
     165             :         pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree(
     166           6 :             new pcl::search::KdTree<pcl::PointXYZ>);
     167           3 :         kdtree->setInputCloud(cloud_pointer);
     168             : 
     169             :         // Euclidean clustering object.
     170           6 :         pcl::EuclideanClusterExtraction<pcl::PointXYZ> cluster_object;
     171             : 
     172             :         // Set cluster tolerance to 5cm (small values may cause objects to be
     173             :         // divided in several clusters, whereas big values may join objects in a
     174             :         // same cluster).
     175           3 :         cluster_object.setClusterTolerance(0.05);
     176             : 
     177             :         // Set the minimum and maximum number of points that a cluster can have.
     178           3 :         cluster_object.setMinClusterSize(2);
     179           3 :         cluster_object.setMaxClusterSize(100);
     180             : 
     181             :         // Set the search method for Euclidian clustering.
     182           3 :         cluster_object.setSearchMethod(kdtree);
     183             : 
     184             :         // Set input for Euclidian clustering.
     185           3 :         cluster_object.setInputCloud(cloud_pointer);
     186             : 
     187             :         // Output vector for detected clusters.
     188           6 :         std::vector<pcl::PointIndices> clusters;
     189             : 
     190             :         // Function to extract clusters from input cloud.
     191           3 :         cluster_object.extract(clusters);
     192             : 
     193             :         // Loop through every found object (cluster of points).
     194          54 :         for (const auto& c : clusters)
     195             :         {
     196             :             // Temp PointCloud for adding to an Object later on.
     197         102 :             pcl::PointCloud<pcl::PointXYZ> temp;
     198             : 
     199             :             // Set the rest of the Object and pointcloud parameters.
     200          51 :             temp.width =
     201          51 :                 static_cast<unsigned int>(cloud_pointer->points.size());
     202          51 :             temp.height = 0;
     203          51 :             temp.is_dense = true;
     204             : 
     205             :             /* Temporary vector containing polar coordinates for all points in
     206             :              * the cluster */
     207         102 :             std::vector<std::pair<double, double>> polar_vector;
     208             : 
     209             :             // Loop through every point in the clustered object.
     210         303 :             for (auto point_signed : c.indices)
     211             :             {
     212         252 :                 const auto point{std::size_t(point_signed)};
     213             :                 // Put Points in temp and push back x and y to temporary vectors
     214             :                 // for calculating average
     215         252 :                 temp.points.push_back(cloud_pointer->points[point]);
     216         252 :                 polar_vector.emplace_back(
     217         504 :                     std::sqrt(
     218         252 :                         double(std::pow(cloud_pointer->points[point].x, 2))
     219         252 :                         + std::pow(cloud_pointer->points[point].y, 2)),
     220         504 :                     std::atan2(double(cloud_pointer->points[point].y),
     221         252 :                                cloud_pointer->points[point].x));
     222             :             }
     223             : 
     224             :             // Sort temporary vector from low to high
     225          51 :             boost::range::sort(polar_vector,
     226         448 :                                [](const auto& lhs, const auto& rhs) {
     227         448 :                                    return lhs.second < rhs.second;
     228         448 :                                });
     229             : 
     230             :             // Calculate distance between the two point with the greatest
     231             :             // distance in between
     232             :             double max_distance;
     233             :             {
     234          51 :                 std::pair<double, double> min_point, max_point;
     235         102 :                 if ((polar_vector.back().second - polar_vector.front().second)
     236          51 :                     <= M_PI)
     237             :                 {
     238          48 :                     min_point = polar_vector.front();
     239          48 :                     max_point = polar_vector.back();
     240             :                 }
     241             :                 else
     242             :                 {
     243             :                     const auto* first_positive_element_iter{
     244           6 :                         &*boost::range::find_if(
     245             :                             polar_vector,
     246          27 :                             [](const auto& e) { return e.second > 0; })};
     247           3 :                     max_point = *first_positive_element_iter;
     248           3 :                     min_point = *--first_positive_element_iter;
     249             :                 }
     250          51 :                 max_distance = std::sqrt(
     251          51 :                     std::pow(max_point.first, 2) + std::pow(min_point.first, 2)
     252          51 :                     - 2 * min_point.first * max_point.first
     253          51 :                           * cos(max_point.second - min_point.second));
     254             :             }
     255             : 
     256             :             // Duplicate points and place them near original points, this is
     257             :             // needed for 3D object
     258          51 :             polar_vector.reserve(polar_vector.size() * 2);
     259          51 :             boost::range::transform(
     260             :                 polar_vector,
     261             :                 std::back_inserter(polar_vector),
     262         504 :                 [max_distance](const auto& e) -> std::pair<double, double> {
     263         504 :                     return {e.first + max_distance, e.second};
     264          51 :                 });
     265             : 
     266             :             // Add height to object and create Polyhedron
     267             :             using Point2 = CGAL::Simple_cartesian<double>::Point_2;
     268             :             using Point3 = CGAL::Simple_cartesian<double>::Point_3;
     269         102 :             std::vector<Point2> front_and_back_points;
     270          51 :             boost::range::transform(polar_vector,
     271             :                                     std::back_inserter(front_and_back_points),
     272         504 :                                     [](const auto& e) {
     273             :                                         return Point2(
     274        1008 :                                             e.first * std::cos(e.second),
     275        1008 :                                             e.first * std::sin(e.second));
     276        1059 :                                     });
     277         102 :             std::vector<Point2> extreme_points;
     278          51 :             CGAL::convex_hull_2(front_and_back_points.begin(),
     279             :                                 front_and_back_points.end(),
     280          51 :                                 std::back_inserter(extreme_points));
     281             : 
     282             :             using Polyhedron = Object::Polyhedron;
     283         102 :             Polyhedron polyhedron;
     284         102 :             std::vector<Polyhedron::Vertex_index> upper_rim, lower_rim;
     285          51 :             boost::range::transform(
     286             :                 extreme_points,
     287             :                 std::back_inserter(upper_rim),
     288         298 :                 [&polyhedron](const auto& p) {
     289         596 :                     return polyhedron.add_vertex(
     290         596 :                         Point3(p.y() / 1000.0, height / 2, p.x() / 1000.0));
     291         647 :                 });
     292          51 :             boost::range::transform(
     293             :                 extreme_points,
     294             :                 std::back_inserter(lower_rim),
     295         298 :                 [&polyhedron](const auto& p) {
     296         596 :                     return polyhedron.add_vertex(
     297         596 :                         Point3(p.y() / 1000.0, -height / 2, p.x() / 1000.0));
     298         647 :                 });
     299             : 
     300             :             // Add 3D points to Polyhedron
     301         647 :             for (std::size_t i = 0, j = i + 1; i < extreme_points.size();
     302         298 :                  ++i, j = (i + 1) % extreme_points.size())
     303             :             {
     304         298 :                 polyhedron.add_face(upper_rim[i], upper_rim[j], lower_rim[i]);
     305         298 :                 polyhedron.add_face(upper_rim[j], lower_rim[j], lower_rim[i]);
     306             :             }
     307             : 
     308             :             // Set the rest of the Object and pointcloud parameters.
     309          51 :             temp.width =
     310          51 :                 static_cast<unsigned int>(cloud_pointer->points.size());
     311          51 :             temp.height = 0;
     312          51 :             temp.is_dense = true;
     313             : 
     314         102 :             Object temp_object;
     315          51 :             temp_object.id = 0;
     316          51 :             temp_object.polyhedron = polyhedron;
     317          51 :             temp_object.speed = std::nullopt;
     318          51 :             temp_object.is_cup = false;
     319          51 :             temp_object.is_deleted = false;
     320             : 
     321             :             // Calculate northest, southest, most west and most east lying point
     322             :             // for calculating average
     323             :             {
     324          51 :                 decltype(extreme_points)::iterator north, south, west, east;
     325          51 :                 CGAL::ch_nswe_point(extreme_points.begin(),
     326             :                                     extreme_points.end(),
     327             :                                     north,
     328             :                                     south,
     329             :                                     west,
     330             :                                     east);
     331             : 
     332             :                 // Calculate middlepoint by taking average.
     333          51 :                 temp_object.middlepoint =
     334         102 :                     Point3((north->y() + south->y()) / 2000.0,
     335         102 :                            static_cast<double>(0),
     336         102 :                            (east->x() + west->x()) / 2000.0);
     337             :             }
     338          51 :             temp_object.point_cloud = temp;
     339             : 
     340             :             // Push information back to pointcloud to return.
     341          51 :             found_objects.push_back(temp_object);
     342             :         }
     343             :     }
     344             :     // Return found objects.
     345           3 :     return found_objects;
     346             : }
     347             : 
     348             : } // namespace PointCloud
     349           3 : } // namespace HLR

Generated by: LCOV version 1.12