LCOV - code coverage report
Current view: top level - src/PointCloud - ObjectInterpreter.cpp (source / functions) Hit Total Coverage
Test: HLR Lines: 121 122 99.2 %
Date: 2018-01-16 14:57:16 Functions: 13 13 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           2 : std::vector<Object> ObjectInterpreter::get_current_objects(
      26             :     const HLR::Vision::CupDetection& detected_cups,
      27             :     const pcl::PointCloud<pcl::PointXYZ>& point_cloud)
      28             : {
      29           2 :     merge_objects(detected_cups, point_cloud);
      30           2 :     return current_objects;
      31             : }
      32             : 
      33           7 : std::vector<Object> ObjectInterpreter::get_changed_objects(
      34             :     const HLR::Vision::CupDetection& detected_cups,
      35             :     const pcl::PointCloud<pcl::PointXYZ>& point_cloud)
      36             : {
      37           7 :     merge_objects(detected_cups, point_cloud);
      38           7 :     std::vector<Object> changed_objects;
      39             : 
      40           7 :     for (const auto& i : current_objects)
      41             :     {
      42           0 :         add_only_changed_objects(i, changed_objects);
      43             :     }
      44             : 
      45           7 :     return changed_objects;
      46             : }
      47             : 
      48           2 : void ObjectInterpreter::add_only_changed_objects(
      49             :     const Object& object,
      50             :     std::vector<Object>& changed_objects) const
      51             : {
      52           2 :     bool is_new = true;
      53           6 :     for (const auto& i : history.front())
      54             :     {
      55           4 :         if (object.id == i.id)
      56             :         {
      57           2 :             is_new = false;
      58           2 :             if (object != i)
      59             :             {
      60           2 :                 changed_objects.push_back(object);
      61             :             }
      62             :         }
      63             :     }
      64           2 :     if (is_new)
      65             :     {
      66           1 :         changed_objects.push_back(object);
      67             :     }
      68           2 : }
      69             : 
      70           9 : void ObjectInterpreter::merge_objects(
      71             :     const HLR::Vision::CupDetection& detected_cups,
      72             :     const pcl::PointCloud<pcl::PointXYZ>& point_cloud)
      73             : {
      74           9 :     history.push_front(std::move(current_objects));
      75           9 :     current_objects = lidar_interpreter.get_objects(detected_cups, point_cloud);
      76           9 : }
      77             : 
      78           3 : std::vector<Object> ObjectInterpreter::isolate_objects(
      79             :     const pcl::PointCloud<pcl::PointXYZ>& newest_cloud)
      80             : {
      81           3 :     constexpr double height = 3.0;
      82             : 
      83             :     // Empty Object vector which is going to contain all objects.
      84             :     // Will be returned at the end of the function.
      85           3 :     std::vector<Object> found_objects;
      86             : 
      87             :     // Check if input contains data to prevent program from crashing.
      88           3 :     if (!newest_cloud.empty())
      89             :     {
      90             :         // This function needs a boost shared_pointer to function.
      91             :         auto cloud_pointer =
      92             :             boost::make_shared<const pcl::PointCloud<pcl::PointXYZ>>(
      93           6 :                 newest_cloud);
      94             :         pcl::PointCloud<pcl::PointXYZ>::Ptr cluster_pointer(
      95           6 :             new pcl::PointCloud<pcl::PointXYZ>);
      96             : 
      97             :         // Variables and setup for search algorithm for Euclidian clustering.
      98             :         pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree(
      99           6 :             new pcl::search::KdTree<pcl::PointXYZ>);
     100           3 :         kdtree->setInputCloud(cloud_pointer);
     101             : 
     102             :         // Euclidean clustering object.
     103           6 :         pcl::EuclideanClusterExtraction<pcl::PointXYZ> cluster_object;
     104             : 
     105             :         // Set cluster tolerance to 5cm (small values may cause objects to be
     106             :         // divided in several clusters, whereas big values may join objects in a
     107             :         // same cluster).
     108           3 :         cluster_object.setClusterTolerance(0.05);
     109             : 
     110             :         // Set the minimum and maximum number of points that a cluster can have.
     111           3 :         cluster_object.setMinClusterSize(2);
     112           3 :         cluster_object.setMaxClusterSize(100);
     113             : 
     114             :         // Set the search method for Euclidian clustering.
     115           3 :         cluster_object.setSearchMethod(kdtree);
     116             : 
     117             :         // Set input for Euclidian clustering.
     118           3 :         cluster_object.setInputCloud(cloud_pointer);
     119             : 
     120             :         // Output vector for detected clusters.
     121           6 :         std::vector<pcl::PointIndices> clusters;
     122             : 
     123             :         // Function to extract clusters from input cloud.
     124           3 :         cluster_object.extract(clusters);
     125             : 
     126             :         // Loop through every found object (cluster of points).
     127          54 :         for (const auto& c : clusters)
     128             :         {
     129             :             // Temp PointCloud for adding to an Object later on.
     130         102 :             pcl::PointCloud<pcl::PointXYZ> temp;
     131             : 
     132             :             // Set the rest of the Object and pointcloud parameters.
     133          51 :             temp.width =
     134          51 :                 static_cast<unsigned int>(cloud_pointer->points.size());
     135          51 :             temp.height = 0;
     136          51 :             temp.is_dense = true;
     137             : 
     138             :             /* Temporary vector containing polar coordinates for all points in
     139             :              * the cluster */
     140         102 :             std::vector<std::pair<double, double>> polar_vector;
     141             : 
     142             :             // Loop through every point in the clustered object.
     143         303 :             for (auto point_signed : c.indices)
     144             :             {
     145         252 :                 const auto point{std::size_t(point_signed)};
     146             :                 // Put Points in temp and push back x and y to temporary vectors
     147             :                 // for calculating average
     148         252 :                 temp.points.push_back(cloud_pointer->points[point]);
     149         252 :                 polar_vector.emplace_back(
     150         504 :                     std::sqrt(
     151         252 :                         double(std::pow(cloud_pointer->points[point].x, 2))
     152         252 :                         + std::pow(cloud_pointer->points[point].y, 2)),
     153         504 :                     std::atan2(double(cloud_pointer->points[point].y),
     154         252 :                                cloud_pointer->points[point].x));
     155             :             }
     156             : 
     157             :             // Sort temporary vector from low to high
     158          51 :             boost::range::sort(polar_vector,
     159         448 :                                [](const auto& lhs, const auto& rhs) {
     160         448 :                                    return lhs.second < rhs.second;
     161         448 :                                });
     162             : 
     163             :             // Calculate distance between the two point with the greatest
     164             :             // distance in between
     165             :             double max_distance;
     166             :             {
     167          51 :                 std::pair<double, double> min_point, max_point;
     168         102 :                 if ((polar_vector.back().second - polar_vector.front().second)
     169          51 :                     <= M_PI)
     170             :                 {
     171          48 :                     min_point = polar_vector.front();
     172          48 :                     max_point = polar_vector.back();
     173             :                 }
     174             :                 else
     175             :                 {
     176             :                     const auto* first_positive_element_iter{
     177           6 :                         &*boost::range::find_if(
     178             :                             polar_vector,
     179          27 :                             [](const auto& e) { return e.second > 0; })};
     180           3 :                     max_point = *first_positive_element_iter;
     181           3 :                     min_point = *--first_positive_element_iter;
     182             :                 }
     183          51 :                 max_distance = std::sqrt(
     184          51 :                     std::pow(max_point.first, 2) + std::pow(min_point.first, 2)
     185          51 :                     - 2 * min_point.first * max_point.first
     186          51 :                           * cos(max_point.second - min_point.second));
     187             :             }
     188             : 
     189             :             // Duplicate points and place them near original points, this is
     190             :             // needed for 3D object
     191          51 :             polar_vector.reserve(polar_vector.size() * 2);
     192          51 :             boost::range::transform(
     193             :                 polar_vector,
     194             :                 std::back_inserter(polar_vector),
     195         504 :                 [max_distance](const auto& e) -> std::pair<double, double> {
     196         504 :                     return {e.first + max_distance, e.second};
     197          51 :                 });
     198             : 
     199             :             // Add height to object and create Polyhedron
     200             :             using Point2 = CGAL::Simple_cartesian<double>::Point_2;
     201             :             using Point3 = CGAL::Simple_cartesian<double>::Point_3;
     202         102 :             std::vector<Point2> front_and_back_points;
     203          51 :             boost::range::transform(polar_vector,
     204             :                                     std::back_inserter(front_and_back_points),
     205         504 :                                     [](const auto& e) {
     206             :                                         return Point2(
     207        1008 :                                             e.first * std::cos(e.second),
     208        1008 :                                             e.first * std::sin(e.second));
     209        1059 :                                     });
     210         102 :             std::vector<Point2> extreme_points;
     211          51 :             CGAL::convex_hull_2(front_and_back_points.begin(),
     212             :                                 front_and_back_points.end(),
     213          51 :                                 std::back_inserter(extreme_points));
     214             : 
     215             :             using Polyhedron = Object::Polyhedron;
     216         102 :             Polyhedron polyhedron;
     217         102 :             std::vector<Polyhedron::Vertex_index> upper_rim, lower_rim;
     218          51 :             boost::range::transform(
     219             :                 extreme_points,
     220             :                 std::back_inserter(upper_rim),
     221         298 :                 [&polyhedron](const auto& p) {
     222         596 :                     return polyhedron.add_vertex(
     223         596 :                         Point3(p.y() / 1000.0, height / 2, p.x() / 1000.0));
     224         647 :                 });
     225          51 :             boost::range::transform(
     226             :                 extreme_points,
     227             :                 std::back_inserter(lower_rim),
     228         298 :                 [&polyhedron](const auto& p) {
     229         596 :                     return polyhedron.add_vertex(
     230         596 :                         Point3(p.y() / 1000.0, -height / 2, p.x() / 1000.0));
     231         647 :                 });
     232             : 
     233             :             // Add 3D points to Polyhedron
     234         647 :             for (std::size_t i = 0, j = i + 1; i < extreme_points.size();
     235         298 :                  ++i, j = (i + 1) % extreme_points.size())
     236             :             {
     237         298 :                 polyhedron.add_face(upper_rim[i], upper_rim[j], lower_rim[i]);
     238         298 :                 polyhedron.add_face(upper_rim[j], lower_rim[j], lower_rim[i]);
     239             :             }
     240             : 
     241             :             // Set the rest of the Object and pointcloud parameters.
     242          51 :             temp.width =
     243          51 :                 static_cast<unsigned int>(cloud_pointer->points.size());
     244          51 :             temp.height = 0;
     245          51 :             temp.is_dense = true;
     246             : 
     247         102 :             Object temp_object;
     248          51 :             temp_object.id = 0;
     249          51 :             temp_object.polyhedron = polyhedron;
     250          51 :             temp_object.speed = std::nullopt;
     251          51 :             temp_object.is_cup = false;
     252          51 :             temp_object.is_deleted = false;
     253             : 
     254             :             // Calculate northest, southest, most west and most east lying point
     255             :             // for calculating average
     256             :             {
     257          51 :                 decltype(extreme_points)::iterator north, south, west, east;
     258          51 :                 CGAL::ch_nswe_point(extreme_points.begin(),
     259             :                                     extreme_points.end(),
     260             :                                     north,
     261             :                                     south,
     262             :                                     west,
     263             :                                     east);
     264             : 
     265             :                 // Calculate middlepoint by taking average.
     266          51 :                 temp_object.middlepoint =
     267         102 :                     Point3((north->y() + south->y()) / 2000.0,
     268         102 :                            static_cast<double>(0),
     269         102 :                            (east->x() + west->x()) / 2000.0);
     270             :             }
     271          51 :             temp_object.point_cloud = temp;
     272             : 
     273             :             // Push information back to pointcloud to return.
     274          51 :             found_objects.push_back(temp_object);
     275             :         }
     276             :     }
     277             :     // Return found objects.
     278           3 :     return found_objects;
     279             : }
     280             : 
     281             : } // namespace PointCloud
     282           3 : } // namespace HLR

Generated by: LCOV version 1.12