b39ae2337966628f9c96f3e2532f68a09dd9b280
[pcl.git] /
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Point Cloud Library (PCL) - www.pointclouds.org
5  * Copyright (c) 2013-, Open Perception, Inc.
6  *
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * * Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * * Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  * * Neither the name of the copyright holder(s) nor the names of its
20  * contributors may be used to endorse or promote products derived
21  * from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  *
36  * main_ground_based_people_detection_app.cpp
37  * Created on: Nov 30, 2012
38  * Author: Matteo Munaro
39  *
40  * Example file for performing people detection on a Kinect live stream.
41  * As a first step, the ground is manually initialized, then people detection is performed with the GroundBasedPeopleDetectionApp class,
42  * which implements the people detection algorithm described here:
43  * M. Munaro, F. Basso and E. Menegatti,
44  * Tracking people within groups with RGB-D data,
45  * In Proceedings of the International Conference on Intelligent Robots and Systems (IROS) 2012, Vilamoura (Portugal), 2012.
46  */
47
48 #include <pcl/console/parse.h>
49 #include <pcl/point_types.h>
50 #include <pcl/visualization/pcl_visualizer.h>    
51 #include <pcl/io/openni_grabber.h>
52 #include <pcl/sample_consensus/sac_model_plane.h>
53 #include <pcl/people/ground_based_people_detection_app.h>
54 #include <pcl/common/time.h>
55
56
57 #include <mutex>
58 #include <thread>
59
60 using namespace std::chrono_literals;
61
62 typedef pcl::PointXYZRGBA PointT;
63 typedef pcl::PointCloud<PointT> PointCloudT;
64
65 // PCL viewer //
66 pcl::visualization::PCLVisualizer viewer("PCL Viewer");
67
68 // Mutex: //
69 std::mutex cloud_mutex;
70
71 enum { COLS = 640, ROWS = 480 };
72
73 int print_help()
74 {
75   std::cout << "*******************************************************" << std::endl;
76   std::cout << "Ground based people detection app options:" << std::endl;
77   std::cout << "   --help    <show_this_help>" << std::endl;
78   std::cout << "   --svm     <path_to_svm_file>" << std::endl;
79   std::cout << "   --conf    <minimum_HOG_confidence (default = -1.5)>" << std::endl;
80   std::cout << "   --min_h   <minimum_person_height (default = 1.3)>" << std::endl;
81   std::cout << "   --max_h   <maximum_person_height (default = 2.3)>" << std::endl;
82   std::cout << "*******************************************************" << std::endl;
83   return 0;
84 }
85
86 void cloud_cb_ (const PointCloudT::ConstPtr &callback_cloud, PointCloudT::Ptr& cloud,
87     bool* new_cloud_available_flag)
88 {
89   cloud_mutex.lock ();    // for not overwriting the point cloud from another thread
90   *cloud = *callback_cloud;
91   *new_cloud_available_flag = true;
92   cloud_mutex.unlock ();
93 }
94
95 struct callback_args{
96   // structure used to pass arguments to the callback function
97   PointCloudT::Ptr clicked_points_3d;
98   pcl::visualization::PCLVisualizer::Ptr viewerPtr;
99 };
100   
101 void
102 pp_callback (const pcl::visualization::PointPickingEvent& event, void* args)
103 {
104   struct callback_args* data = (struct callback_args *)args;
105   if (event.getPointIndex () == -1)
106     return;
107   PointT current_point;
108   event.getPoint(current_point.x, current_point.y, current_point.z);
109   data->clicked_points_3d->points.push_back(current_point);
110   // Draw clicked points in red:
111   pcl::visualization::PointCloudColorHandlerCustom<PointT> red (data->clicked_points_3d, 255, 0, 0);
112   data->viewerPtr->removePointCloud("clicked_points");
113   data->viewerPtr->addPointCloud(data->clicked_points_3d, red, "clicked_points");
114   data->viewerPtr->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 10, "clicked_points");
115   std::cout << current_point.x << " " << current_point.y << " " << current_point.z << std::endl;
116 }
117
118 int main (int argc, char** argv)
119 {
120   if(pcl::console::find_switch (argc, argv, "--help") || pcl::console::find_switch (argc, argv, "-h"))
121         return print_help();
122
123   // Algorithm parameters:
124   std::string svm_filename = "../../people/data/trainedLinearSVMForPeopleDetectionWithHOG.yaml";
125   float min_confidence = -1.5;
126   float min_height = 1.3;
127   float max_height = 2.3;
128   float voxel_size = 0.06;
129   Eigen::Matrix3f rgb_intrinsics_matrix;
130   rgb_intrinsics_matrix << 525, 0.0, 319.5, 0.0, 525, 239.5, 0.0, 0.0, 1.0; // Kinect RGB camera intrinsics
131
132   // Read if some parameters are passed from command line:
133   pcl::console::parse_argument (argc, argv, "--svm", svm_filename);
134   pcl::console::parse_argument (argc, argv, "--conf", min_confidence);
135   pcl::console::parse_argument (argc, argv, "--min_h", min_height);
136   pcl::console::parse_argument (argc, argv, "--max_h", max_height);
137
138   // Read Kinect live stream:
139   PointCloudT::Ptr cloud (new PointCloudT);
140   bool new_cloud_available_flag = false;
141   pcl::Grabber* interface = new pcl::OpenNIGrabber();
142   std::function<void (const pcl::PointCloud<pcl::PointXYZRGBA>::ConstPtr&)> f =
143       [&] (const pcl::PointCloud<pcl::PointXYZRGBA>::ConstPtr& callback_cloud) { cloud_cb_ (callback_cloud, cloud, &new_cloud_available_flag); };
144   interface->registerCallback (f);
145   interface->start ();
146
147   // Wait for the first frame:
148   while(!new_cloud_available_flag) 
149     std::this_thread::sleep_for(1ms);
150   new_cloud_available_flag = false;
151
152   cloud_mutex.lock ();    // for not overwriting the point cloud
153
154   // Display pointcloud:
155   pcl::visualization::PointCloudColorHandlerRGBField<PointT> rgb(cloud);
156   viewer.addPointCloud<PointT> (cloud, rgb, "input_cloud");
157   viewer.setCameraPosition(0,0,-2,0,-1,0,0);
158
159   // Add point picking callback to viewer:
160   struct callback_args cb_args;
161   PointCloudT::Ptr clicked_points_3d (new PointCloudT);
162   cb_args.clicked_points_3d = clicked_points_3d;
163   cb_args.viewerPtr = pcl::visualization::PCLVisualizer::Ptr(&viewer);
164   viewer.registerPointPickingCallback (pp_callback, (void*)&cb_args);
165   std::cout << "Shift+click on three floor points, then press 'Q'..." << std::endl;
166
167   // Spin until 'Q' is pressed:
168   viewer.spin();
169   std::cout << "done." << std::endl;
170   
171   cloud_mutex.unlock ();
172
173   // Ground plane estimation:
174   Eigen::VectorXf ground_coeffs;
175   ground_coeffs.resize(4);
176   std::vector<int> clicked_points_indices;
177   for (unsigned int i = 0; i < clicked_points_3d->size(); i++)
178     clicked_points_indices.push_back(i);
179   pcl::SampleConsensusModelPlane<PointT> model_plane(clicked_points_3d);
180   model_plane.computeModelCoefficients(clicked_points_indices,ground_coeffs);
181   std::cout << "Ground plane: " << ground_coeffs(0) << " " << ground_coeffs(1) << " " << ground_coeffs(2) << " " << ground_coeffs(3) << std::endl;
182
183   // Initialize new viewer:
184   pcl::visualization::PCLVisualizer viewer("PCL Viewer");          // viewer initialization
185   viewer.setCameraPosition(0,0,-2,0,-1,0,0);
186
187   // Create classifier for people detection:  
188   pcl::people::PersonClassifier<pcl::RGB> person_classifier;
189   person_classifier.loadSVMFromFile(svm_filename);   // load trained SVM
190
191   // People detection app initialization:
192   pcl::people::GroundBasedPeopleDetectionApp<PointT> people_detector;    // people detection object
193   people_detector.setVoxelSize(voxel_size);                        // set the voxel size
194   people_detector.setIntrinsics(rgb_intrinsics_matrix);            // set RGB camera intrinsic parameters
195   people_detector.setClassifier(person_classifier);                // set person classifier
196   people_detector.setPersonClusterLimits(min_height, max_height, 0.1, 8.0);  // set person classifier
197 //  people_detector.setSensorPortraitOrientation(true);             // set sensor orientation to vertical
198
199   // For timing:
200   static unsigned count = 0;
201   static double last = pcl::getTime ();
202
203   // Main loop:
204   while (!viewer.wasStopped())
205   {
206     if (new_cloud_available_flag && cloud_mutex.try_lock ())    // if a new cloud is available
207     {
208       new_cloud_available_flag = false;
209
210       // Perform people detection on the new cloud:
211       std::vector<pcl::people::PersonCluster<PointT> > clusters;   // vector containing persons clusters
212       people_detector.setInputCloud(cloud);
213       people_detector.setGround(ground_coeffs);                    // set floor coefficients
214       people_detector.compute(clusters);                           // perform people detection
215
216       ground_coeffs = people_detector.getGround();                 // get updated floor coefficients
217
218       // Draw cloud and people bounding boxes in the viewer:
219       viewer.removeAllPointClouds();
220       viewer.removeAllShapes();
221       pcl::visualization::PointCloudColorHandlerRGBField<PointT> rgb(cloud);
222       viewer.addPointCloud<PointT> (cloud, rgb, "input_cloud");
223       unsigned int k = 0;
224       for(std::vector<pcl::people::PersonCluster<PointT> >::iterator it = clusters.begin(); it != clusters.end(); ++it)
225       {
226         if(it->getPersonConfidence() > min_confidence)             // draw only people with confidence above a threshold
227         {
228           // draw theoretical person bounding box in the PCL viewer:
229           it->drawTBoundingBox(viewer, k);
230           k++;
231         }
232       }
233       std::cout << k << " people found" << std::endl;
234       viewer.spinOnce();
235
236       // Display average framerate:
237       if (++count == 30)
238       {
239         double now = pcl::getTime ();
240         std::cout << "Average framerate: " << double(count)/double(now - last) << " Hz" <<  std::endl;
241         count = 0;
242         last = now;
243       }
244       cloud_mutex.unlock ();
245     }
246   }
247
248   return 0;
249 }