00001 #include <target_object_detector.hpp>
00002 #include <Eigen/Core>
00003 #include <Eigen/Eigen>
00004 #include <Eigen/Dense>
00005 #include <algorithm>
00006 #include "libsvm/svm.h"
00007 #include <ros/package.h>
00008 using namespace pcl;
00009
00010 EuclideanCluster::EuclideanCluster(ros::NodeHandle nh, ros::NodeHandle n)
00011 : nh_(nh),
00012 rate_(n.param("loop_rate", 10)),
00013 frame_id_(n.param<std::string>("clustering_frame_id", "base_link"))
00014 {
00015 source_pc_sub_ = nh_.subscribe(n.param<std::string>("source_pc_topic_name", "/hokuyo3d/hokuyo_cloud2"), 1, &EuclideanCluster::EuclideanCallback, this);
00016 fileterd_cloud_pub_ = nh_.advertise<sensor_msgs::PointCloud2>(n.param<std::string>("filtered_pc_topic_name", "/filtered_pointcloud"), 1);
00017 euclidean_cluster_pub_ = nh_.advertise<jsk_recognition_msgs::BoundingBoxArray>(n.param<std::string>("box_name", "/clustering_result"), 1);
00018
00019
00020 n.param<double>("clusterTolerance", clusterTolerance_, 0.02);
00021 n.param<int>("minSize", minSize_, 100);
00022 n.param<int>("maxSize", maxSize_, 25000);
00023
00024 n.param<float>("crop_x_min", crop_min_.x, 5.5);
00025 n.param<float>("crop_x_max", crop_max_.x, -5.5);
00026 n.param<float>("crop_y_min", crop_min_.y, -5.5);
00027 n.param<float>("crop_y_max", crop_max_.y, 5.5);
00028 n.param<float>("crop_z_min", crop_min_.z, -0.1);
00029 n.param<float>("crop_z_max", crop_max_.z, 0.5);
00030 n.param<std::string>("svm_model_path", svm_model_path_,
00031 ros::package::getPath("target_object_detector")
00032 + "/model/train.csv.model");
00033 if((model_=svm_load_model(svm_model_path_.c_str()))==0)
00034 {
00035 fprintf(stderr,"can't open model file %s\n",svm_model_path_.c_str());
00036 exit(1);
00037 }
00038 n.param<std::string>("svm_range_path", svm_range_path_,
00039 ros::package::getPath("target_object_detector")
00040 + "/model/train.csv.range");
00041 read_scaling_parameters(svm_range_path_);
00042 }
00043
00044 void EuclideanCluster::EuclideanCallback(
00045 const sensor_msgs::PointCloud2::ConstPtr &source_pc) {
00046
00047
00048
00049 sensor_msgs::PointCloud2 trans_pc;
00050 try {
00051 pcl_ros::transformPointCloud(frame_id_, *source_pc, trans_pc, tf_);
00052 } catch (tf::ExtrapolationException e) {
00053 ROS_ERROR("pcl_ros::transformPointCloud %s", e.what());
00054 }
00055
00056
00057 pcl::PointCloud<PointXYZI> pcl_source;
00058 pcl::fromROSMsg(trans_pc, pcl_source);
00059 pcl::PointCloud<PointXYZI>::Ptr pcl_source_ptr(new pcl::PointCloud<PointXYZI>(pcl_source));
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073 CropBox(pcl_source_ptr, crop_min_, crop_max_);
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083 Clustering(pcl_source_ptr);
00084 }
00085
00086 void EuclideanCluster::CropBox(pcl::PointCloud<pcl::PointXYZI>::Ptr cloud,
00087 pcl::PointXYZ min, pcl::PointXYZ max) {
00088 Eigen::Vector4f minPoint;
00089
00090 minPoint[0] = min.x;
00091 minPoint[1] = min.y;
00092 minPoint[2] = min.z;
00093
00094 Eigen::Vector4f maxPoint;
00095 maxPoint[0] = max.x;
00096 maxPoint[1] = max.y;
00097 maxPoint[2] = max.z;
00098
00099 Eigen::Vector3f boxTranslatation;
00100 boxTranslatation[0] = 0;
00101 boxTranslatation[1] = 0;
00102 boxTranslatation[2] = 0;
00103
00104 Eigen::Vector3f boxRotation;
00105 boxRotation[0] = 0;
00106 boxRotation[1] = 0;
00107 boxRotation[2] = 0;
00108
00109 Eigen::Affine3f boxTransform;
00110
00111 pcl::CropBox<pcl::PointXYZI> cropFilter;
00112 cropFilter.setInputCloud(cloud);
00113 cropFilter.setMin(minPoint);
00114 cropFilter.setMax(maxPoint);
00115 cropFilter.setTranslation(boxTranslatation);
00116 cropFilter.setRotation(boxRotation);
00117
00118 pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZI>());
00119 cropFilter.filter(*cloud_filtered);
00120 pcl::copyPointCloud<pcl::PointXYZI, pcl::PointXYZI>(*cloud_filtered, *cloud);
00121 }
00122
00123 void EuclideanCluster::Clustering(pcl::PointCloud<pcl::PointXYZI>::Ptr cloud) {
00124 pcl::search::KdTree<pcl::PointXYZI>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZI>);
00125 tree->setInputCloud(cloud);
00126
00127 std::vector<pcl::PointIndices> cluster_indices;
00128 pcl::EuclideanClusterExtraction<pcl::PointXYZI> ec;
00129 ec.setClusterTolerance(clusterTolerance_);
00130 ec.setMinClusterSize(minSize_);
00131 ec.setMaxClusterSize(maxSize_);
00132 ec.setSearchMethod(tree);
00133 ec.setInputCloud(cloud);
00134 ec.extract(cluster_indices);
00135
00136 int j = 0;
00137 jsk_recognition_msgs::BoundingBoxArray box_array;
00138
00139 for (std::vector<pcl::PointIndices>::const_iterator it = cluster_indices.begin(); it != cluster_indices.end(); ++it) {
00140 pcl::PointCloud<pcl::PointXYZI>::Ptr cloud_cluster(new pcl::PointCloud<pcl::PointXYZI>);
00141 for (std::vector<int>::const_iterator pit = it->indices.begin(); pit != it->indices.end(); ++pit){
00142 cloud_cluster->points.push_back(cloud->points[*pit]);
00143 }
00144 cloud_cluster->width = cloud_cluster->points.size();
00145 cloud_cluster->height = 1;
00146 cloud_cluster->is_dense = true;
00147
00148
00149 svm_node features[14];
00150 feature_calculation(cloud_cluster, features);
00151 ROS_INFO_STREAM("features are extracted");
00152 int is_target_object = static_cast<int>( svm_predict( model_, features ) );
00153 ROS_INFO_STREAM("is_target_object : " << is_target_object);
00154 if (is_target_object == 1) {
00155
00156 jsk_recognition_msgs::BoundingBox box;
00157 box = MinAreaRect(cloud_cluster, j);
00158 box_array.boxes.push_back(box);
00159 j++;
00160 }
00161 }
00162
00163
00164 ROS_INFO("Found %lu clusters:", cluster_indices.size());
00165
00166
00167 box_array.header.stamp = ros::Time::now();
00168 box_array.header.frame_id = frame_id_;
00169 euclidean_cluster_pub_.publish(box_array);
00170
00171
00172 cluster_indices.clear();
00173 }
00174
00175 jsk_recognition_msgs::BoundingBox EuclideanCluster::MinAreaRect(pcl::PointCloud<pcl::PointXYZI>::Ptr cloud, int cluster_cnt){
00176
00177 pcl::MomentOfInertiaEstimation<pcl::PointXYZI> feature_extractor;
00178 feature_extractor.setInputCloud(cloud);
00179 feature_extractor.compute();
00180
00181 std::vector<float> moment_of_inertia;
00182 std::vector<float> eccentricity;
00183 pcl::PointXYZI min_point_AABB;
00184 pcl::PointXYZI max_point_AABB;
00185
00186 geometry_msgs::Pose pose;
00187 geometry_msgs::Vector3 size;
00188
00189 feature_extractor.getMomentOfInertia(moment_of_inertia);
00190 feature_extractor.getEccentricity(eccentricity);
00191 feature_extractor.getAABB(min_point_AABB, max_point_AABB);
00192
00193
00194 std::vector<cv::Point2f> points;
00195 for(unsigned int i = 0; i < cloud->points.size(); i++){
00196 cv::Point2f p2d;
00197 p2d.x = cloud->points[i].x;
00198 p2d.y = cloud->points[i].y;
00199 points.push_back(p2d);
00200 }
00201 cv::Mat points_mat(points);
00202 cv::RotatedRect rrect = cv::minAreaRect(points_mat);
00203
00204
00205
00206
00207
00208
00209 pose.position.x = rrect.center.x;
00210 pose.position.y = rrect.center.y;
00211 pose.position.z = (min_point_AABB.z + max_point_AABB.z) / 2.0;
00212
00213 Eigen::Matrix3f AxisAngle;
00214 Eigen::Vector3f axis(0,0,1);
00215 AxisAngle = Eigen::AngleAxisf(rrect.angle*M_PI/180.0, axis);
00216 Eigen::Quaternionf quat(AxisAngle);
00217 pose.orientation.x = quat.x();
00218 pose.orientation.y = quat.y();
00219 pose.orientation.z = quat.z();
00220 pose.orientation.w = quat.w();
00221
00222 size.x = rrect.size.width;
00223 size.y = rrect.size.height;
00224 size.z = max_point_AABB.z - min_point_AABB.z;
00225
00226
00227 std::stringstream ss;
00228 std::string object_name;
00229 ss << cluster_cnt;
00230 object_name = "object_" + ss.str();
00231
00232 br_.sendTransform(tf::StampedTransform(
00233 tf::Transform(
00234 tf::Quaternion(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w),
00235 tf::Vector3(pose.position.x, pose.position.y, max_point_AABB.z)),
00236 ros::Time::now(), "world", object_name));
00237
00238 jsk_recognition_msgs::BoundingBox box;
00239 box.header.frame_id = frame_id_;
00240 box.pose = pose;
00241 box.dimensions = size;
00242 box.label = cluster_cnt;
00243
00244 return box;
00245 }
00246
00247
00248
00249 void EuclideanCluster::run()
00250 {
00251 while(nh_.ok()){
00252 ros::spinOnce();
00253 rate_.sleep();
00254 }
00255 }
00256
00257
00258 void EuclideanCluster::feature_calculation(pcl::PointCloud<pcl::PointXYZI>::Ptr cloud,
00259 svm_node *features)
00260 {
00261 int n;
00262 float f21,f22,f23,f24,f25,f26;
00263 float v1, v2, v3;
00264 double v11, v22, v33;
00265 float f31,f32,f33,f34,f35,f36,f37;
00266 pcl::PointXYZI min_point;
00267 pcl::PointXYZI max_point;
00268 std::vector<float> moment_of_inertia;
00269 std::vector<float> eccentricity;
00270 Eigen::Matrix3f cmat;
00271 Eigen::Vector4f xyz_centroid;
00272 EIGEN_ALIGN16 Eigen::Matrix3f covariance_matrix;
00273 pcl::MomentOfInertiaEstimation <pcl::PointXYZI> feature_extractor;
00274 feature_extractor.setInputCloud (cloud);
00275 feature_extractor.compute ();
00276 feature_extractor.getMomentOfInertia (moment_of_inertia);
00277 feature_extractor.getEccentricity (eccentricity);
00278 feature_extractor.getAABB (min_point, max_point);
00279 pcl::compute3DCentroid (*cloud, xyz_centroid);
00280 pcl::computeCovarianceMatrix (*cloud, xyz_centroid, covariance_matrix);
00281 n = cloud->points.size();
00282 f21 = covariance_matrix (0,0) / (n - 1);
00283 f22 = covariance_matrix (0,1) / (n - 1);
00284 f23 = covariance_matrix (0,2) / (n - 1);
00285 f24 = covariance_matrix (1,1) / (n - 1);
00286 f25 = covariance_matrix (1,2) / (n - 1);
00287 f26 = covariance_matrix (2,2) / (n - 1);
00288 cmat (0,0) = f21;
00289 cmat (0,1) = f22;
00290 cmat (0,2) = f23;
00291 cmat (1,0) = f22;
00292 cmat (1,1) = f24;
00293 cmat (1,2) = f25;
00294 cmat (2,0) = f23;
00295 cmat (2,1) = f25;
00296 cmat (2,2) = f26;
00297 Eigen::EigenSolver<Eigen::MatrixXf> es(cmat, false);
00298 std::complex<double> lambda1 = es.eigenvalues()[0];
00299 std::complex<double> lambda2 = es.eigenvalues()[1];
00300 std::complex<double> lambda3 = es.eigenvalues()[2];
00301 v11 = lambda1.real();
00302 v22 = lambda2.real();
00303 v33 = lambda3.real();
00304 std::vector<double> v;
00305 v.push_back (v11);
00306 v.push_back (v22);
00307 v.push_back (v33);
00308 std::sort(v.begin(), v.end() );
00309 v3 = v[0];
00310 v2 = v[1];
00311 v1 = v[2];
00312 v.clear();
00313
00314 double nnn = v1 * v2 * v3;
00315 f31 = (v1 - v2) / v1;
00316 f32 = (v2 - v3) / v1;
00317 f33 = v3 / v1;
00318 f34 = pow(nnn, 1.0 / 3.0);
00319 f35 = (v1 - v3) / v1;
00320 f36 = -(v1 * log(v1) + v2 * log(v2) + v3 * log(v3));
00321 f37 = v3 / (v1 + v2 + v3);
00322
00323 features[0].value = f21;
00324 features[1].value = f22;
00325 features[2].value = f23;
00326 features[3].value = f24;
00327 features[4].value = f25;
00328 features[5].value = f26;
00329 features[6].value = f31;
00330 features[7].value = f32;
00331 features[8].value = f33;
00332 features[9].value = f34;
00333 features[10].value = f35;
00334 features[11].value = f36;
00335 features[12].value = f37;
00336 features[0].index = 1;
00337 features[1].index = 2;
00338 features[2].index = 3;
00339 features[3].index = 4;
00340 features[4].index = 5;
00341 features[5].index = 6;
00342 features[6].index = 7;
00343 features[7].index = 8;
00344 features[8].index = 9;
00345 features[9].index = 10;
00346 features[10].index = 11;
00347 features[11].index = 12;
00348 features[12].index = 13;
00349 features[13].index = -1;
00350 normlize_features(features);
00351 for (int i = 0; i < 13; ++i) {
00352 std::cout << features[i].value << ", ";
00353 }
00354 std::cout << "\n";
00355
00356 }
00357
00358
00359 void EuclideanCluster::normlize_features(svm_node *features)
00360 {
00361 for (int i = 0; i < 13; ++i) {
00362 if (feature_max_[i] == feature_min_[i]) {
00363 continue;
00364 }
00365 if(features[i].value == feature_min_[i]){
00366 features[i].value = feature_lower_;
00367 }else if(features[i].value == feature_max_[i]){
00368 features[i].value = feature_upper_;
00369 }else{
00370 features[i].value = feature_lower_ + (feature_upper_ - feature_lower_)*
00371 (features[i].value - feature_min_[i])/
00372 (feature_max_[i] - feature_min_[i]);
00373 }
00374 }
00375 }
00376
00377
00378 void EuclideanCluster::read_scaling_parameters(std::string scaling_parameter_file)
00379 {
00380
00381
00382 std::ifstream ifs(scaling_parameter_file.c_str());
00383 std::string str;
00384 if (ifs.fail())
00385 {
00386 ROS_ERROR("failed to read scaling parameter file");
00387 return;
00388 }
00389
00390
00391 if(!getline(ifs, str))
00392 {
00393 ROS_ERROR("failed to read first line on scaling parameter file");
00394 return;
00395 }
00396
00397
00398 if(!getline(ifs, str))
00399 {
00400 ROS_ERROR("failed to read second line on scaling parameter file");
00401 return;
00402 }
00403
00404 std::vector<std::string> splited = split(str, ' ');
00405
00406 feature_lower_ = atof(splited[0].c_str());
00407 feature_upper_ = atof(splited[1].c_str());
00408
00409
00410 while(getline(ifs, str))
00411 {
00412 std::vector<std::string> splited = split(str, ' ');
00413
00414 feature_min_.push_back(atof(splited[1].c_str()));
00415 feature_max_.push_back(atof(splited[2].c_str()));
00416 }
00417 }
00418
00419 std::vector<std::string> EuclideanCluster::split(const std::string &s, char delim)
00420 {
00421 std::vector<std::string> elems;
00422 std::stringstream ss(s);
00423 std::string item;
00424 while (getline(ss, item, delim)) {
00425 if (!item.empty()) {
00426 elems.push_back(item);
00427 }
00428 }
00429 return elems;
00430 }