shape_features.cpp
Go to the documentation of this file.
00001 #include <sstream>
00002 #include <iostream>
00003 #include <fstream>
00004 #include <tabletop_pushing/shape_features.h>
00005 #include <pcl16_ros/transforms.h>
00006 #include <pcl16/ros/conversions.h>
00007 #include <pcl16/surface/concave_hull.h>
00008 #include <pcl16/common/pca.h>
00009 // #include <pcl16/registration/transformation_estimation_svd.h>
00010 #include <pcl16/registration/transformation_estimation_lm.h>
00011 #include <cpl_visual_features/comp_geometry.h>
00012 #include <cpl_visual_features/helpers.h>
00013 #include <iostream>
00014 
00015 #define XY_RES 0.00075
00016 #define DRAW_LR_LIMITS 1
00017 // #define USE_RANGE_AND_VAR_FEATS 1
00018 
00019 using namespace cpl_visual_features;
00020 using tabletop_pushing::ProtoObject;
00021 typedef tabletop_pushing::VisFeedbackPushTrackingFeedback PushTrackerState;
00022 
00023 namespace tabletop_pushing
00024 {
00025 
00026 inline int getHistBinIdx(int x_idx, int y_idx, int n_x_bins, int n_y_bins)
00027 {
00028   return x_idx*n_y_bins+y_idx;
00029 }
00030 
00031 inline int worldLocToIdx(double val, double min_val, double max_val)
00032 {
00033   return round((val-min_val)/XY_RES);
00034 }
00035 
00036 inline int objLocToIdx(double val, double min_val, double max_val)
00037 {
00038   return round((val-min_val)/XY_RES);
00039 }
00040 
00041 cv::Point worldPtToImgPt(pcl16::PointXYZ world_pt, double min_x, double max_x,
00042                          double min_y, double max_y)
00043 {
00044   cv::Point img_pt(worldLocToIdx(world_pt.x, min_x, max_x),
00045                    worldLocToIdx(world_pt.y, min_y, max_y));
00046   return img_pt;
00047 }
00048 
00049 pcl16::PointXYZ worldPointInObjectFrame(pcl16::PointXYZ world_pt, PushTrackerState& cur_state)
00050 {
00051   // Center on object frame
00052   pcl16::PointXYZ shifted_pt;
00053   shifted_pt.x = world_pt.x - cur_state.x.x;
00054   shifted_pt.y = world_pt.y - cur_state.x.y;
00055   shifted_pt.z = world_pt.z - cur_state.z;
00056   double ct = cos(cur_state.x.theta);
00057   double st = sin(cur_state.x.theta);
00058   // Rotate into correct frame
00059   pcl16::PointXYZ obj_pt;
00060   obj_pt.x =  ct*shifted_pt.x + st*shifted_pt.y;
00061   obj_pt.y = -st*shifted_pt.x + ct*shifted_pt.y;
00062   obj_pt.z = shifted_pt.z; // NOTE: Currently assume 2D motion
00063   return obj_pt;
00064 }
00065 
00066 std::vector<int> getJumpIndices(XYZPointCloud& concave_hull, double alpha)
00067 {
00068   std::vector<int> jump_indices;
00069   for (int i = 0; i < concave_hull.size(); i++)
00070   {
00071     if (dist(concave_hull[i], concave_hull[(i+1)%concave_hull.size()]) > 2.0*alpha)
00072     {
00073       jump_indices.push_back(i);
00074     }
00075   }
00076   return jump_indices;
00077 }
00078 
00079 XYZPointCloud getObjectBoundarySamples(ProtoObject& cur_obj, double hull_alpha)
00080 {
00081   // Get 2D projection of object
00082   // TODO: Remove the z, then add it back after finding the hull... how do we do this?
00083   XYZPointCloud footprint_cloud(cur_obj.cloud);
00084   for (int i = 0; i < footprint_cloud.size(); ++i)
00085   {
00086     // HACK: This is a complete hack, based on the current table height used in pushing.
00087     footprint_cloud.at(i).z = -0.3;
00088   }
00089 
00090   // TODO: Examine sensitivity of hull_alpha...
00091   XYZPointCloud hull_cloud;
00092   pcl16::ConcaveHull<pcl16::PointXYZ> hull;
00093   hull.setDimension(2);  // NOTE: Get 2D projection of object
00094   hull.setInputCloud(footprint_cloud.makeShared());
00095   hull.setAlpha(hull_alpha);
00096   hull.reconstruct(hull_cloud);
00097   return hull_cloud;
00098 }
00099 
00100 cpl_visual_features::Path compareBoundaryShapes(XYZPointCloud& hull_a, XYZPointCloud& hull_b,
00101                                                 double& min_cost, double epsilon_cost)
00102 {
00103   Samples2f samples_a, samples_b;
00104   for (unsigned int i = 0; i < hull_a.size(); ++i)
00105   {
00106     cv::Point2f pt(hull_a[i].x, hull_a[i].y);
00107     samples_a.push_back(pt);
00108   }
00109   for (unsigned int i = 0; i < hull_b.size(); ++i)
00110   {
00111     cv::Point2f pt(hull_b[i].x, hull_b[i].y);
00112     samples_b.push_back(pt);
00113   }
00114   // TODO: Expose these parameters
00115   int radius_bins = 5;
00116   int theta_bins = 12;
00117   ShapeDescriptors descriptors_a = constructDescriptors<Samples2f>(samples_a, radius_bins, theta_bins);
00118   ShapeDescriptors descriptors_b = constructDescriptors<Samples2f>(samples_b, radius_bins, theta_bins);
00119   // ROS_INFO_STREAM("Computing cost matrix");
00120   cv::Mat cost_mat = computeCostMatrix(descriptors_a, descriptors_b, epsilon_cost);
00121   cv::imshow("cost matrix", cost_mat);
00122   cpl_visual_features::Path path;
00123   // ROS_INFO_STREAM("Getting min cost path");
00124   min_cost = getMinimumCostPath(cost_mat, path);
00125 
00126   return path;
00127 }
00128 
00129 void estimateTransformFromMatches(XYZPointCloud& cloud_t_0, XYZPointCloud& cloud_t_1,
00130                                   cpl_visual_features::Path p, Eigen::Matrix4f& transform, double max_dist)
00131 {
00132   // Do least squares estimate of the change
00133   std::vector<int> source_indices;
00134   std::vector<int> target_indices;
00135   // Get inliers
00136   // TODO: Make sure have at least 1
00137   // TODO: Make max dist the average distance of matches? (median+epsilon?)
00138   // ROS_INFO_STREAM("Path has: " << p.size() << " elements");
00139   // ROS_INFO_STREAM("cloud_t_0 has: " << cloud_t_0.size() << " elements");
00140   // ROS_INFO_STREAM("cloud_t_1 has: " << cloud_t_1.size() << " elements");
00141   for (int i = 0; i < p.size(); ++i)
00142   {
00143     // NOTE: Filter out matches with distance greater than max_dist
00144     if(i < cloud_t_0.size() && p[i] < cloud_t_1.size() &&
00145        PointCloudSegmentation::sqrDist(cloud_t_0.at(i), cloud_t_1.at(p[i])) < max_dist)
00146     {
00147       source_indices.push_back(i);
00148       target_indices.push_back(p[i]);
00149     }
00150   }
00151   ROS_INFO_STREAM("Path had " << p.size() << " matches.\tNow have " << source_indices.size() << " matches.");
00152   pcl16::registration::TransformationEstimationLM<pcl16::PointXYZ, pcl16::PointXYZ> tlm;
00153   tlm.estimateRigidTransformation(cloud_t_0, source_indices, cloud_t_1, target_indices, transform);
00154 }
00155 
00156 cv::Mat visualizeObjectBoundarySamples(XYZPointCloud& hull_cloud, PushTrackerState& cur_state)
00157 {
00158   double max_y = 0.3;
00159   double min_y = -0.3;
00160   double max_x = 0.3;
00161   double min_x = -0.3;
00162   int rows = ceil((max_y-min_y)/XY_RES);
00163   int cols = ceil((max_x-min_x)/XY_RES);
00164   cv::Mat footprint(rows, cols, CV_8UC3, cv::Scalar(255,255,255));
00165 
00166   for (int i = 0; i < hull_cloud.size(); ++i)
00167   {
00168     pcl16::PointXYZ obj_pt =  worldPointInObjectFrame(hull_cloud[i], cur_state);
00169     int img_x = objLocToIdx(obj_pt.x, min_x, max_x);
00170     int img_y = objLocToIdx(obj_pt.y, min_y, max_y);
00171     cv::Scalar color(128, 0, 0);
00172     cv::circle(footprint, cv::Point(img_x, img_y), 1, color, 3);
00173   }
00174   return footprint;
00175 }
00176 
00177 cv::Mat visualizeObjectBoundaryMatches(XYZPointCloud& hull_a, XYZPointCloud& hull_b,
00178                                        PushTrackerState& cur_state, cpl_visual_features::Path& path)
00179 {
00180   double max_y = 0.3;
00181   double min_y = -0.3;
00182   double max_x = 0.3;
00183   double min_x = -0.3;
00184   double max_displacement = 100;
00185   int rows = ceil((max_y-min_y)/XY_RES);
00186   int cols = ceil((max_x-min_x)/XY_RES);
00187   cv::Mat match_img(rows, cols, CV_8UC3, cv::Scalar(255,255,255));
00188 
00189   cv::Scalar blue(128, 0, 0);
00190   cv::Scalar green(0, 128, 0);
00191   cv::Scalar red(0, 0, 128);
00192 
00193   for (int i = 0; i < hull_a.size(); ++i)
00194   {
00195     pcl16::PointXYZ start_point_world = hull_a[i];
00196     pcl16::PointXYZ end_point_world = hull_b[path[i]];
00197     // Transform to display frame
00198     pcl16::PointXYZ start_point_obj = worldPointInObjectFrame(start_point_world, cur_state);
00199     pcl16::PointXYZ end_point_obj = worldPointInObjectFrame(end_point_world, cur_state);
00200     cv::Point start_point(objLocToIdx(start_point_obj.x, min_x, max_x),
00201                           objLocToIdx(start_point_obj.y, min_y, max_y));
00202     cv::Point end_point(objLocToIdx(end_point_obj.x, min_x, max_x),
00203                         objLocToIdx(end_point_obj.y, min_y, max_y));
00204 
00205     // Draw green point for previous, blue point for current
00206     cv::circle(match_img, start_point, 1, green, 3);
00207     cv::circle(match_img, end_point, 1, blue, 3);
00208     // Draw red line between matches
00209     if (end_point.x > 0 && end_point.x < match_img.rows &&
00210         end_point.y > 0 && end_point.x < match_img.cols &&
00211         std::abs(start_point.x - end_point.x) +
00212         std::abs(start_point.y - end_point.y) < max_displacement)
00213     {
00214       cv::line(match_img, start_point, end_point, red);
00215     }
00216 
00217   }
00218   return match_img;
00219 }
00220 
00221 cv::Mat visualizeObjectContactLocation(XYZPointCloud& hull_cloud, PushTrackerState& cur_state,
00222                                        pcl16::PointXYZ& contact_pt, pcl16::PointXYZ& forward_pt)
00223 {
00224   double max_y = 0.3;
00225   double min_y = -0.3;
00226   double max_x = 0.3;
00227   double min_x = -0.3;
00228 
00229   cv::Mat footprint = visualizeObjectBoundarySamples(hull_cloud, cur_state);
00230   pcl16::PointXYZ contact_pt_obj =  worldPointInObjectFrame(contact_pt, cur_state);
00231   int img_x = objLocToIdx(contact_pt_obj.x, min_x, max_x);
00232   int img_y = objLocToIdx(contact_pt_obj.y, min_y, max_y);
00233   pcl16::PointXYZ forward_pt_obj =  worldPointInObjectFrame(forward_pt, cur_state);
00234   int img_x_1 = objLocToIdx(forward_pt_obj.x, min_x, max_x);
00235   int img_y_1 = objLocToIdx(forward_pt_obj.y, min_y, max_y);
00236   cv::Scalar color(0, 0, 128);
00237   cv::line(footprint, cv::Point(img_x, img_y), cv::Point(img_x_1, img_y_1), color, 1);
00238   cv::circle(footprint, cv::Point(img_x, img_y), 3, color, 3);
00239   cv::circle(footprint, cv::Point(img_x_1, img_y_1), 3, color, 3);
00240   // ROS_WARN_STREAM("World point: (" << contact_pt.x << ", " << contact_pt.y << ")");
00241   // ROS_WARN_STREAM("Obj point: (" << contact_pt_obj.x << ", " << contact_pt_obj.y << ")");
00242   // ROS_WARN_STREAM("Img point: (" << img_x << ", " << img_y << ")");
00243   // ROS_WARN_STREAM("World point: (" << forward_pt.x << ", " << forward_pt.y << ")");
00244   // ROS_WARN_STREAM("Obj point: (" << forward_pt_obj.x << ", " << forward_pt_obj.y << ")");
00245   // ROS_WARN_STREAM("Img point: (" << img_x_1 << ", " << img_y_1 << ")");
00246   return footprint;
00247 }
00248 
00249 cv::Mat getObjectFootprint(cv::Mat obj_mask, pcl16::PointCloud<pcl16::PointXYZ>& cloud)
00250 {
00251   cv::Mat kernel(5,5,CV_8UC1, 255);
00252   cv::Mat obj_mask_target;
00253   obj_mask.copyTo(obj_mask_target);
00254   // Perform close
00255   cv::dilate(obj_mask, obj_mask_target, kernel);
00256   cv::erode(obj_mask_target, obj_mask, kernel);
00257   // cv::erode(obj_mask, obj_mask, kernel);
00258   double min_x = 300., max_x = -300.;
00259   double min_y = 300., max_y = -300.;
00260 
00261   for (int r = 0; r < obj_mask.rows; ++r)
00262   {
00263     for (int c = 0; c < obj_mask.cols; ++c)
00264     {
00265       if (obj_mask.at<uchar>(r,c) > 0)
00266       {
00267         double x = cloud.at(c,r).x;
00268         double y = cloud.at(c,r).y;
00269         if (x < min_x) min_x = x;
00270         if (x > max_x) max_x = x;
00271         if (y < min_y) min_y = y;
00272         if (y > max_y) max_y = y;
00273       }
00274     }
00275   }
00276   int rows = ceil((max_y-min_y)/XY_RES);
00277   int cols = ceil((max_x-min_x)/XY_RES);
00278   cv::Mat footprint(rows, cols, CV_8UC1, cv::Scalar(0));
00279   for (int r = 0; r < obj_mask.rows; ++r)
00280   {
00281     for (int c = 0; c < obj_mask.cols; ++c)
00282     {
00283       if (obj_mask.at<uchar>(r,c) > 0)
00284       {
00285         // TODO: Allow different max z in image.
00286         double x = cloud.at(c,r).x;
00287         double y = cloud.at(c,r).y;
00288         if (isnan(x) || isnan(y))
00289           continue;
00290         int img_x = worldLocToIdx(x, min_x, max_x);
00291         int img_y = worldLocToIdx(y, min_y, max_y);
00292         if (img_y > rows ||img_x > cols || img_y < 0 || img_x < 0)
00293         {
00294           ROS_WARN_STREAM("Image index out of bounds! at (" << img_y << ", " << img_x <<
00295                           ", " << y << ", " << x << ")");
00296           ROS_WARN_STREAM("X extremes: (" << min_x << ", " << max_x << ")");
00297           ROS_WARN_STREAM("Y extremes: (" << min_y << ", " << max_y << ")");
00298           ROS_WARN_STREAM("image size: (" << rows << ", " << cols << ")");
00299         }
00300         footprint.at<uchar>(img_y, img_x) = 255;
00301       }
00302     }
00303   }
00304   // Perform close
00305   cv::dilate(footprint, footprint, kernel);
00306   cv::erode(footprint, footprint, kernel);
00307   // Perform open
00308   cv::erode(footprint, footprint, kernel);
00309   cv::dilate(footprint, footprint, kernel);
00310   return footprint;
00311 }
00312 
00313 ShapeLocations extractObjectShapeContext(ProtoObject& cur_obj, bool use_center)
00314 {
00315   XYZPointCloud samples_pcl = getObjectBoundarySamples(cur_obj);
00316   return extractShapeContextFromSamples(samples_pcl, cur_obj, use_center);
00317 }
00318 
00319 ShapeLocations extractShapeContextFromSamples(XYZPointCloud& samples_pcl, ProtoObject& cur_obj, bool use_center)
00320 {
00321   Samples2f samples;
00322   for (unsigned int i = 0; i < samples_pcl.size(); ++i)
00323   {
00324     cv::Point2f pt(samples_pcl[i].x, samples_pcl[i].y);
00325     samples.push_back(pt);
00326   }
00327   int radius_bins = 5;
00328   int theta_bins = 12;
00329   double max_radius = 0.5;
00330   cv::Point2f center(cur_obj.centroid[0], cur_obj.centroid[1]);
00331   ShapeDescriptors descriptors = constructDescriptors(samples, center, use_center, radius_bins, theta_bins, max_radius);
00332   ShapeLocations locs;
00333   for (unsigned int i = 0; i < descriptors.size(); ++i)
00334   {
00335     ShapeLocation loc(samples_pcl[i], descriptors[i]);
00336     locs.push_back(loc);
00337   }
00338   // computeShapeFeatureAffinityMatrix(locs, use_center);
00339   return locs;
00340 }
00341 
00342 cv::Mat makeHistogramImage(ShapeDescriptor histogram, int n_x_bins, int n_y_bins, int bin_width_pixels)
00343 {
00344   cv::Mat hist_img(n_x_bins*bin_width_pixels+1, n_y_bins*bin_width_pixels+1, CV_8UC1, cv::Scalar(255));
00345   int max_count = 10;
00346   for (int i = 0; i < histogram.size(); ++i)
00347   {
00348     if (histogram[i] == 0) continue;
00349     int pix_val = (1 - histogram[i]/max_count)*255;
00350     int start_x = (i%n_x_bins)*bin_width_pixels;
00351     int start_y = (i/n_x_bins)*bin_width_pixels;;
00352     // ROS_INFO_STREAM("(start_x, start_y): (" << start_x << ", " << start_y << ")");
00353     for (int x = start_x; x < start_x+bin_width_pixels; ++x)
00354     {
00355       for (int y = start_y; y < start_y+bin_width_pixels; ++y)
00356       {
00357         hist_img.at<uchar>(y, x) = pix_val;
00358       }
00359     }
00360   }
00361   // Draw hist bin columns
00362   for (int i = 0; i <= n_x_bins; ++i)
00363   {
00364     int x = i*bin_width_pixels;
00365     for (int y = 0; y < hist_img.rows; ++y)
00366     {
00367       hist_img.at<uchar>(y,x) = 0;
00368     }
00369   }
00370   // Draw hist bin rows
00371   for (int i = 0; i <= n_y_bins; ++i)
00372   {
00373     int y = i*bin_width_pixels;
00374     for (int x = 0; x < hist_img.cols; ++x)
00375     {
00376       hist_img.at<uchar>(y,x) = 0;
00377     }
00378   }
00379   // cv::imshow("local_hist_img", hist_img);
00380   // cv::waitKey();
00381   return hist_img;
00382 }
00383 void drawSamplePoints(XYZPointCloud& hull, XYZPointCloud& samples, double alpha, pcl16::PointXYZ& center_pt,
00384                       pcl16::PointXYZ& sample_pt, pcl16::PointXYZ& approach_pt,
00385                       pcl16::PointXYZ e_left, pcl16::PointXYZ e_right,
00386                       pcl16::PointXYZ c_left, pcl16::PointXYZ c_right,
00387                       pcl16::PointXYZ i_left, pcl16::PointXYZ i_right,
00388                       double x_res, double y_res, double x_range, double y_range, ProtoObject& cur_obj)
00389 {
00390   double max_y = 0.5;
00391   double min_y = -0.2;
00392   double max_x = 1.0;
00393   double min_x = 0.2;
00394   // TODO: Make function to get cv::Size from (max_x, min_x, max_y, min_y, XY_RES)
00395   // TODO: Make sure everything is getting drawn
00396   int rows = ceil((max_y-min_y)/XY_RES);
00397   int cols = ceil((max_x-min_x)/XY_RES);
00398   cv::Mat footprint(rows, cols, CV_8UC3, cv::Scalar(255,255,255));
00399   std::vector<int> jump_indices = getJumpIndices(hull, alpha);
00400   for (int i = 0; i < hull.size(); ++i)
00401   {
00402     int j = (i+1) % hull.size();
00403     bool is_jump = false;
00404     for (int k = 0; k < jump_indices.size(); ++k)
00405     {
00406       if (i == jump_indices[k])
00407       {
00408         is_jump = true;
00409       }
00410     }
00411     if (is_jump)
00412     {
00413       continue;
00414     }
00415     pcl16::PointXYZ obj_pt0 = hull[i];
00416     pcl16::PointXYZ obj_pt1 = hull[j];
00417     cv::Point img_pt0 = worldPtToImgPt(obj_pt0, min_x, max_x, min_y, max_y);
00418     cv::Point img_pt1 = worldPtToImgPt(obj_pt1, min_x, max_x, min_y, max_y);
00419     cv::Scalar color(0, 0, 128);
00420     cv::circle(footprint, img_pt0, 1, color, 3);
00421     cv::line(footprint, img_pt0, img_pt1, color, 1);
00422   }
00423   for (int i = 0; i < samples.size(); ++i)
00424   {
00425     cv::Point img_pt = worldPtToImgPt(samples[i], min_x, max_x, min_y, max_y);
00426     cv::Scalar color(0, 255, 0);
00427     cv::circle(footprint, img_pt, 1, color,3);
00428     cv::circle(footprint, img_pt, 3, color,3);
00429   }
00430   cv::Point img_center = worldPtToImgPt(center_pt, min_x, max_x, min_y, max_y);
00431   cv::Point img_approach_pt = worldPtToImgPt(approach_pt, min_x, max_x, min_y, max_y);
00432   cv::Point img_sample_pt = worldPtToImgPt(sample_pt, min_x, max_x, min_y, max_y);
00433   cv::Point e_left_img = worldPtToImgPt(e_left, min_x, max_x, min_y, max_y);
00434   cv::Point e_right_img = worldPtToImgPt(e_right, min_x, max_x, min_y, max_y);
00435   cv::Point c_left_img = worldPtToImgPt(c_left, min_x, max_x, min_y, max_y);
00436   cv::Point c_right_img = worldPtToImgPt(c_right, min_x, max_x, min_y, max_y);
00437   cv::Point i_left_img = worldPtToImgPt(i_left, min_x, max_x, min_y, max_y);
00438   cv::Point i_right_img = worldPtToImgPt(i_right, min_x, max_x, min_y, max_y);
00439 
00440   cv::Mat footprint_hist;
00441   footprint.copyTo(footprint_hist);
00442 
00443   cv::line(footprint, img_sample_pt, img_center, cv::Scalar(255,0,0), 2);
00444   cv::line(footprint, e_left_img, e_right_img, cv::Scalar(255,0,0), 2);
00445   // cv::line(footprint, e_left_img, c_left_img, cv::Scalar(255,0,255));
00446   // cv::line(footprint, e_right_img, c_right_img, cv::Scalar(0,255,255));
00447   cv::circle(footprint, img_center, 3, cv::Scalar(255,0,0), 2);
00448   // cv::circle(footprint, img_approach_pt, 3, cv::Scalar(255,0,0),2);
00449   // cv::circle(footprint, e_left_img, 3, cv::Scalar(255,0,255));
00450   // cv::circle(footprint, e_right_img, 3, cv::Scalar(0,255,255));
00451 
00452 
00453   // Draw sample point last
00454   // cv::line(footprint, img_sample_pt, img_center, cv::Scalar(255,255,255));
00455   cv::circle(footprint, img_sample_pt, 1, cv::Scalar(255,0,255),3);
00456   cv::circle(footprint, img_sample_pt, 3, cv::Scalar(255,0,255),3);
00457   // cv::circle(footprint, i_left_img, 3, cv::Scalar(255,0,255));
00458   // cv::circle(footprint, i_right_img, 3, cv::Scalar(0,255,255));
00459 
00460   int n_x_bins = ceil(x_range/x_res);
00461   int n_y_bins = ceil(y_range/y_res);
00462   double min_hist_x = -x_range*0.5;
00463   double min_hist_y = -y_range*0.5;
00464   std::vector<std::vector<cv::Point> > hist_corners;
00465   double center_angle = std::atan2(center_pt.y - sample_pt.y, center_pt.x - sample_pt.x);
00466   double ct = std::cos(center_angle);
00467   double st = std::sin(center_angle);
00468   for (int i = 0; i <= n_x_bins; ++i)
00469   {
00470     double local_x = min_hist_x+x_res*i;
00471     std::vector<cv::Point> hist_row;
00472     hist_corners.push_back(hist_row);
00473     for (int j = 0; j <= n_y_bins; ++j)
00474     {
00475       double local_y = min_hist_y+y_res*j;
00476 
00477       // Transform into world frame
00478       pcl16::PointXYZ corner_in_world;
00479       corner_in_world.x = (local_x*ct - local_y*st ) + sample_pt.x;
00480       corner_in_world.y = (local_x*st + local_y*ct ) + sample_pt.y;
00481       // Transform into image frame
00482       cv::Point corner_in_img = worldPtToImgPt(corner_in_world, min_x, max_x, min_y, max_y);
00483       // draw point
00484       cv::Scalar color(0,0,0);
00485       cv::circle(footprint_hist, corner_in_img, 1, color);
00486       // Draw grid lines
00487       if (i > 0)
00488       {
00489         cv::line(footprint_hist, corner_in_img, hist_corners[i-1][j], color);
00490       }
00491       if (j > 0)
00492       {
00493         cv::line(footprint_hist, corner_in_img, hist_corners[i][j-1], color);
00494       }
00495       hist_corners[i].push_back(corner_in_img);
00496     }
00497   }
00498 
00499   // cv::imshow("local samples", footprint);
00500   // cv::imshow("local hist", footprint_hist);
00501   // char c = cv::waitKey();
00502   // if (c == 'w')
00503   // {
00504   //   std::stringstream boundary_img_write_path;
00505   //   std::stringstream hist_img_write_path;
00506   //   int rand_int = rand();
00507   //   boundary_img_write_path << "/home/thermans/Desktop/" << rand_int << "_boundary_img_.png";
00508   //   hist_img_write_path << "/home/thermans/Desktop/" << rand_int << "_boundary_hist_img.png";
00509   //   std::cout << "Writing " << boundary_img_write_path.str() << " to disk";
00510   //   cv::imwrite(boundary_img_write_path.str(), footprint);
00511   //   std::cout << "Writing " << hist_img_write_path.str() << " to disk";
00512   //   cv::imwrite(hist_img_write_path.str(), footprint_hist);
00513 
00514   //   // HACK: Huge hack just recompute descriptor to write to disk like others
00515   //   XYZPointCloud transformed_pts = transformSamplesIntoSampleLocFrame(samples, cur_obj, sample_pt);
00516   //   ShapeDescriptor histogram = extractPointHistogramXY(transformed_pts, x_res, y_res, x_range, y_range);
00517   //   int bin_width_pixels = 10;
00518   //   cv::Mat hist_img = makeHistogramImage(histogram, n_x_bins, n_y_bins, bin_width_pixels);
00519   //   std::stringstream out_file_name;
00520   //   out_file_name << "/home/thermans/Desktop/" << rand_int << "_local_hist_img.png";
00521   //   cv::imwrite(out_file_name.str(), hist_img);
00522   // }
00523 
00524 }
00525 
00526 XYZPointCloud getLocalSamplesNew(XYZPointCloud& hull, ProtoObject& cur_obj, pcl16::PointXYZ sample_pt,
00527                               double sample_spread, double alpha)
00528 {
00529   // TODO: This is going to get all points in front of the gripper, not only those on the close boundary
00530   double radius = sample_spread / 2.0;
00531   pcl16::PointXYZ center_pt(cur_obj.centroid[0], cur_obj.centroid[1], cur_obj.centroid[2]);
00532   double center_angle = std::atan2(center_pt.y - sample_pt.y, center_pt.x - sample_pt.x);
00533   double approach_dist = 0.05;
00534   pcl16::PointXYZ approach_pt(sample_pt.x - std::cos(center_angle)*approach_dist,
00535                               sample_pt.y - std::sin(center_angle)*approach_dist, 0.0);
00536   pcl16::PointXYZ e_vect(std::cos(center_angle+M_PI/2.0)*radius,
00537                          std::sin(center_angle+M_PI/2.0)*radius, 0.0);
00538   pcl16::PointXYZ e_left(approach_pt.x + e_vect.x, approach_pt.y + e_vect.y, 0.0);
00539   pcl16::PointXYZ e_right(approach_pt.x - e_vect.x, approach_pt.y - e_vect.y, 0.0);
00540   pcl16::PointXYZ c_left(center_pt.x + std::cos(center_angle)*approach_dist + e_vect.x,
00541                          center_pt.y + std::sin(center_angle)*approach_dist + e_vect.y, 0.0);
00542   pcl16::PointXYZ c_right(center_pt.x + std::cos(center_angle)*approach_dist - e_vect.x,
00543                           center_pt.y + std::sin(center_angle)*approach_dist - e_vect.y, 0.0);
00544   std::vector<int> jump_indices = getJumpIndices(hull, alpha);
00545 
00546   std::vector<int> inside_indices;
00547   for (int i = 0; i < cur_obj.cloud.size(); i++)
00548   {
00549     double dist_r = pointLineDistance2D(cur_obj.cloud[i], e_right, c_right);
00550     double dist_l = pointLineDistance2D(cur_obj.cloud[i], e_left, c_left);
00551     if (dist_r > sample_spread  || dist_l > sample_spread)
00552     {
00553       // pcl16::PointXYZ intersection;
00554       // bool intersects_l = lineSegmentIntersection2D(hull[i], hull[(i+1)%hull.size()], e_left, c_left,
00555       //                                               intersection);
00556       // bool intersects_r = lineSegmentIntersection2D(hull[i], hull[(i+1)%hull.size()], e_right, c_right,
00557       //                                               intersection);
00558       // if (intersects_l || intersects_r)
00559       // {
00560       //   inside_indices.push_back(i);
00561       // }
00562     }
00563     else
00564     {
00565       inside_indices.push_back(i);
00566     }
00567   }
00568 
00569   std::vector<int> local_indices;
00570   for (int i = 0; i < inside_indices.size(); ++i)
00571   {
00572     const int idx = inside_indices[i];
00573     // Get projection of this point in gripper y
00574     double dist_l = pointLineDistance2D(cur_obj.cloud[idx], e_left, c_left);
00575     pcl16::PointXYZ e_vect_scaled(std::cos(center_angle+M_PI/2.0)*radius-dist_l,
00576                                   std::sin(center_angle+M_PI/2.0)*radius-dist_l, 0.0);
00577     pcl16::PointXYZ e_pt(approach_pt.x + e_vect.x, approach_pt.y + e_vect.y, 0.0);
00578 
00579     // Check if the line segment between the gripper and this point intersects any other line segment
00580     bool intersects = false;
00581     for (int j = 0; j < hull.size(); ++j)
00582     {
00583       bool j_is_jump = false;
00584       for (int k = 0; k < jump_indices.size(); ++k)
00585       {
00586         if (jump_indices[k] == j)
00587         {
00588           j_is_jump = true;
00589         }
00590       }
00591       // Don't test jump indices for blocking, or ourself
00592       // if (j_is_jump || j == idx || j == idx-1 || j == (idx+1)%hull.size())
00593       // {
00594       //   continue;
00595       // }
00596 
00597       // Do the line test
00598       pcl16::PointXYZ intersection;
00599       if (lineSegmentIntersection2D(e_pt, cur_obj.cloud[idx], hull[j], hull[(j+1)%hull.size()], intersection))
00600       {
00601         intersects = true;
00602       }
00603     }
00604     if (!intersects)
00605     {
00606       local_indices.push_back(idx);
00607       // local_indices.push_back((idx+1)%hull.size());
00608     }
00609   }
00610 
00611   // Copy to new cloud and return
00612   XYZPointCloud local_samples;
00613   pcl16::copyPointCloud(cur_obj.cloud, local_indices, local_samples);
00614   int min_l_idx = 0;
00615   int min_r_idx = 0;
00616   // drawSamplePoints(hull, local_samples, center_pt, sample_pt, approach_pt, e_left, e_right,
00617   //                  c_left, c_right);
00618 
00619   // TODO: Transform samples into sample_loc frame
00620   return local_samples;
00621 }
00622 
00623 XYZPointCloud getLocalSamples(XYZPointCloud& hull, ProtoObject& cur_obj, pcl16::PointXYZ sample_pt,
00624                                  double sample_spread, double alpha)
00625 {
00626   // TODO: This is going to get all points in front of the gripper, not only those on the close boundary
00627   double radius = sample_spread / 2.0;
00628   pcl16::PointXYZ center_pt(cur_obj.centroid[0], cur_obj.centroid[1], cur_obj.centroid[2]);
00629   double center_angle = std::atan2(center_pt.y - sample_pt.y, center_pt.x - sample_pt.x);
00630   double approach_dist = 0.05;
00631   pcl16::PointXYZ approach_pt(sample_pt.x - std::cos(center_angle)*approach_dist,
00632                               sample_pt.y - std::sin(center_angle)*approach_dist, 0.0);
00633   pcl16::PointXYZ e_vect(std::cos(center_angle+M_PI/2.0)*radius,
00634                          std::sin(center_angle+M_PI/2.0)*radius, 0.0);
00635   pcl16::PointXYZ e_left(sample_pt.x + e_vect.x, sample_pt.y + e_vect.y, 0.0);
00636   pcl16::PointXYZ e_right(sample_pt.x - e_vect.x, sample_pt.y - e_vect.y, 0.0);
00637   pcl16::PointXYZ c_left(center_pt.x + std::cos(center_angle)*approach_dist + e_vect.x,
00638                          center_pt.y + std::sin(center_angle)*approach_dist + e_vect.y, 0.0);
00639   pcl16::PointXYZ c_right(center_pt.x + std::cos(center_angle)*approach_dist - e_vect.x,
00640                           center_pt.y + std::sin(center_angle)*approach_dist - e_vect.y, 0.0);
00641   // ROS_INFO_STREAM("center_pt: " << center_pt);
00642   // ROS_INFO_STREAM("sample_pt: " << sample_pt);
00643   // ROS_INFO_STREAM("approach_pt: " << approach_pt);
00644   // ROS_INFO_STREAM("e_vect: " << e_vect);
00645   // ROS_INFO_STREAM("e_left: " << e_left);
00646   // ROS_INFO_STREAM("e_right: " << e_right);
00647 
00648   double min_sample_pt_dist = FLT_MAX;
00649   int sample_pt_idx = -1;
00650   std::vector<int> jump_indices;
00651   for (int i = 0; i < hull.size(); i++)
00652   {
00653     // SAMPLE PT
00654     double sample_pt_dist = dist(sample_pt, hull[i]);
00655     if (sample_pt_dist < min_sample_pt_dist)
00656     {
00657       min_sample_pt_dist = sample_pt_dist;
00658       sample_pt_idx = i;
00659     }
00660     if (dist(hull[i], hull[(i+1)%hull.size()]) > 2.0*alpha)
00661     {
00662       jump_indices.push_back(i);
00663       // ROS_INFO_STREAM("Jump from " << i << " to " << (i+1)%hull.size());
00664     }
00665   }
00666 
00667   // Test intersection of gripper end point rays and all line segments on the object boundary
00668   pcl16::PointXYZ l_intersection;
00669   pcl16::PointXYZ r_intersection;
00670   pcl16::PointXYZ c_intersection;
00671   double min_l_dist = FLT_MAX;
00672   double min_r_dist = FLT_MAX;
00673   double min_c_dist = FLT_MAX;
00674   int min_l_idx = -1;
00675   int min_r_idx = -1;
00676   int min_c_idx = -1;
00677 
00678   double min_far_l_dist = FLT_MAX;
00679   double min_far_r_dist = FLT_MAX;
00680   int far_l_idx = -1;
00681   int far_r_idx = -1;
00682   // TODO: Break this up into a couple of functions
00683   for (int i = 0; i < hull.size(); i++)
00684   {
00685     int idx0 = i;
00686     int idx1 = (i+1) % hull.size();
00687 
00688     // FAR LEFT PT
00689     double far_l_dist = pointLineDistance2D(hull[idx0], e_left, c_left);
00690     if (far_l_dist < min_far_l_dist)
00691     {
00692       far_l_idx = idx0;
00693       min_far_l_dist = far_l_dist;
00694     }
00695 
00696     // FAR RIGHT PT
00697     double far_r_dist = pointLineDistance2D(hull[idx0], e_right, c_right);
00698     if (far_r_dist < min_far_r_dist)
00699     {
00700       far_r_idx = idx0;
00701       min_far_r_dist = far_r_dist;
00702     }
00703 
00704     bool is_jump = false;
00705     for (int j = 0; j < jump_indices.size(); ++j)
00706     {
00707       if (jump_indices[j] == idx0)
00708       {
00709         is_jump = true;
00710       }
00711     }
00712     if (is_jump)
00713     {
00714       continue;
00715     }
00716 
00717     pcl16::PointXYZ intersection;
00718     // LEFT INTERSECTION
00719     if (lineSegmentIntersection2D(hull[idx0], hull[idx1], e_left, c_left, intersection))
00720     {
00721       double pt_dist = dist(intersection, e_left);
00722       if (pt_dist < min_l_dist)
00723       {
00724         min_l_dist = pt_dist;
00725         min_l_idx = i;
00726         l_intersection = intersection;
00727       }
00728     }
00729     // RIGHT INTERSECTION
00730     if (lineSegmentIntersection2D(hull[idx0], hull[idx1], e_right, c_right, intersection))
00731     {
00732       double pt_dist = dist(intersection, e_right);
00733       if (pt_dist < min_r_dist)
00734       {
00735         min_r_dist = pt_dist;
00736         min_r_idx = i;
00737         r_intersection = intersection;
00738       }
00739     }
00740     // CENTER INTERSECTION
00741     if (lineSegmentIntersection2D(hull[idx0], hull[idx1], approach_pt, center_pt, intersection))
00742     {
00743       double pt_dist = dist(intersection, approach_pt);
00744       if (pt_dist < min_c_dist)
00745       {
00746         min_c_dist = pt_dist;
00747         min_c_idx = i;
00748         c_intersection = intersection;
00749       }
00750     }
00751   }
00752 
00753   double sample_pt_dist = dist(approach_pt, sample_pt);
00754   // Default to smaple_pt if no intersection also
00755   if (true || min_c_idx == -1 || sample_pt_dist <= min_c_dist)
00756   {
00757     min_c_idx = sample_pt_idx;
00758     min_c_dist = sample_pt_dist;
00759   }
00760   // Default far left if no left intersection
00761   if (min_l_idx == -1)
00762   {
00763     min_l_idx = far_l_idx;
00764     min_l_dist = min_far_l_dist;
00765   }
00766   // Default far right if no right intersection
00767   if (min_r_idx == -1)
00768   {
00769     min_r_idx = far_r_idx;
00770     min_r_dist = min_far_r_dist;
00771   }
00772 
00773   std::vector<int> indices;
00774   // indices.push_back(min_l_idx);
00775   // indices.push_back((min_l_idx+1) % hull.size());
00776   // indices.push_back(min_r_idx);
00777   // indices.push_back((min_r_idx+1) % hull.size());
00778 
00779   int start_idx = min_l_idx < min_r_idx ? min_l_idx : min_r_idx;
00780   int end_idx = min_l_idx < min_r_idx ? min_r_idx : min_l_idx;
00781   if (min_c_idx >= start_idx && min_c_idx <= end_idx)
00782   {
00783     // Good:
00784     // ROS_INFO_STREAM("Walking inside");
00785   }
00786   else
00787   {
00788     // ROS_INFO_STREAM("Walking outside");
00789     int tmp_idx = start_idx;
00790     start_idx = end_idx;
00791     end_idx = tmp_idx;
00792   }
00793   int start_chunk = jump_indices.size();
00794   int center_chunk = jump_indices.size();
00795   int end_chunk = jump_indices.size();
00796   for (int i = jump_indices.size()-1; i >= 0; --i)
00797   {
00798     if (start_idx <= jump_indices[i])
00799     {
00800       start_chunk = i;
00801     }
00802     if (min_c_idx <= jump_indices[i])
00803     {
00804       center_chunk = i;
00805     }
00806     if (end_idx <= jump_indices[i])
00807     {
00808       end_chunk = i;
00809     }
00810   }
00811   // TODO: Dont walk through unimportant chunks
00812   // ROS_INFO_STREAM("Start chunk is: " << start_chunk);
00813   // ROS_INFO_STREAM("Center chunk is: " << center_chunk);
00814   // ROS_INFO_STREAM("End chunk is: " << end_chunk);
00815 
00816   // ROS_INFO_STREAM("far_l_dist is: " << min_far_l_dist << " at " << far_l_idx);
00817   // ROS_INFO_STREAM("far_r_dist is: " << min_far_r_dist << " at " << far_r_idx);
00818   // ROS_INFO_STREAM("min_l_dist is: " << min_l_dist << " at " << min_l_idx);
00819   // ROS_INFO_STREAM("min_r_dist is: " << min_r_dist << " at " << min_r_idx);
00820   // ROS_INFO_STREAM("min_c_dist is: " << min_c_dist << " at " << min_c_idx);
00821   // ROS_INFO_STREAM("sample_pt_dist is : " << sample_pt_dist << " at " << sample_pt_idx);
00822   // ROS_INFO_STREAM("start idx: " << start_idx);
00823   // ROS_INFO_STREAM("center idx: " << min_c_idx);
00824   // ROS_INFO_STREAM("end idx: " << end_idx);
00825 
00826   int cur_chunk = start_chunk;
00827   // Walk from one intersection to the other through the centroid
00828   for (int i = start_idx; i != end_idx; i = (i+1) % hull.size())
00829   {
00830     double dist_r = pointLineDistance2D(hull[i], e_right, c_right);
00831     double dist_l = pointLineDistance2D(hull[i], e_left, c_left);
00832     // Thorw out points outside the gripper channel
00833     if (dist_r > sample_spread  || dist_l > sample_spread)
00834     {
00835     }
00836     else if (cur_chunk == start_chunk || cur_chunk == end_chunk || cur_chunk == center_chunk)
00837     {
00838       indices.push_back(i);
00839     }
00840     for (int j = 0; j < jump_indices.size(); ++j)
00841     {
00842       if(jump_indices[j] == i)
00843       {
00844         cur_chunk += 1;
00845         cur_chunk = cur_chunk % jump_indices.size();
00846       }
00847     }
00848   }
00849 
00850   // Copy to new cloud and return
00851   XYZPointCloud local_samples;
00852   pcl16::copyPointCloud(hull, indices, local_samples);
00853   double x_res = 0.01, y_res=0.01, x_range=2.0*sample_spread, y_range=2.0*sample_spread;
00854   drawSamplePoints(hull, local_samples, alpha, center_pt, sample_pt, approach_pt, e_left, e_right,
00855                    c_left, c_right, hull[min_l_idx], hull[min_r_idx], x_res, y_res, x_range, y_range, cur_obj);
00856   return local_samples;
00857 }
00858 
00859 XYZPointCloud transformSamplesIntoSampleLocFrame(XYZPointCloud& samples, ProtoObject& cur_obj,
00860                                                  pcl16::PointXYZ sample_pt)
00861 {
00862   XYZPointCloud samples_transformed(samples);
00863   pcl16::PointXYZ center_pt(cur_obj.centroid[0], cur_obj.centroid[1], cur_obj.centroid[2]);
00864   double center_angle = std::atan2(center_pt.y - sample_pt.y, center_pt.x - sample_pt.x);
00865   for (int i = 0; i < samples.size(); ++i)
00866   {
00867     // Remove mean and rotate based on pushing_direction
00868     pcl16::PointXYZ demeaned(samples[i].x - sample_pt.x, samples[i].y - sample_pt.y, samples[i].z);
00869     double ct = std::cos(center_angle);
00870     double st = std::sin(center_angle);
00871     samples_transformed[i].x =  ct*demeaned.x + st*demeaned.y;
00872     samples_transformed[i].y = -st*demeaned.x + ct*demeaned.y;
00873     samples_transformed[i].z = demeaned.z;
00874   }
00875   return samples_transformed;
00876 }
00877 
00878 ShapeDescriptor extractPointHistogramXY(XYZPointCloud& samples, double x_res, double y_res, double x_range,
00879                                         double y_range)
00880 {
00881   int n_x_bins = ceil(x_range/x_res);
00882   int n_y_bins = ceil(y_range/y_res);
00883   ShapeDescriptor hist(n_y_bins*n_x_bins, 0);
00884   // Assume demeaned
00885   for (int i = 0; i < samples.size(); ++i)
00886   {
00887     double x_norm = (samples[i].x+x_range*0.5)/ x_range;
00888     double y_norm = (samples[i].y+y_range*0.5)/ y_range;
00889     if (x_norm  > 1.0 || x_norm < 0 || y_norm > 1.0 || y_norm <0)
00890     {
00891       continue;
00892     }
00893     int x_idx = (int)floor(x_norm*n_x_bins);
00894     int y_idx = (int)floor(y_norm*n_y_bins);
00895     int idx =  getHistBinIdx(x_idx, y_idx, n_x_bins, n_y_bins);
00896     hist[idx] += 1;
00897   }
00898   std::stringstream descriptor;
00899   int feat_sum = 0;
00900   for (int i = 0; i < n_x_bins; ++i)
00901   {
00902     for (int j = 0; j < n_y_bins; ++j)
00903     {
00904       int idx = getHistBinIdx(i, j, n_x_bins, n_y_bins);
00905       descriptor << hist[idx] << " ";
00906       feat_sum += hist[idx];
00907     }
00908     descriptor << "\n";
00909   }
00910   // ROS_INFO_STREAM("Descriptor: \n" << descriptor.str());
00911   // ROS_INFO_STREAM("Descriptor size: " << feat_sum << "\tsample size: " << samples.size());
00912   return hist;
00913 }
00914 
00915 void getPointRangesXY(XYZPointCloud& samples, ShapeDescriptor& sd)
00916 {
00917   double x_min = FLT_MAX;
00918   double x_max = FLT_MIN;
00919   double y_min = FLT_MAX;
00920   double y_max = FLT_MIN;
00921   for (int i = 0; i < samples.size(); ++i)
00922   {
00923     if (samples[i].x > x_max)
00924     {
00925       x_max = samples[i].x;
00926     }
00927     if (samples[i].x < x_min)
00928     {
00929       x_min = samples[i].x;
00930     }
00931     if (samples[i].y > y_max)
00932     {
00933       y_max = samples[i].y;
00934     }
00935     if (samples[i].y < y_min)
00936     {
00937       y_min = samples[i].y;
00938     }
00939   }
00940   double x_range = x_max - x_min;
00941   double y_range = y_max - y_min;
00942   // ROS_INFO_STREAM("x_range: " << x_range << " : (" << x_min << ", " << x_max << ")");
00943   // ROS_INFO_STREAM("y_range: " << y_range << " : (" << y_min << ", " << y_max << ")");
00944   sd.push_back(x_range);
00945   sd.push_back(y_range);
00946 }
00947 
00948 void getCovarianceXYFromPoints(XYZPointCloud& pts, ShapeDescriptor& sd)
00949 {
00950   Eigen::Matrix<float, 4, 1> centroid;
00951   if(pcl16::compute3DCentroid(pts, centroid) != 0)
00952   {
00953     Eigen::Matrix3f covariance;
00954     if(pcl16::computeCovarianceMatrix(pts, centroid, covariance) != 0)
00955     {
00956       std::stringstream disp_stream;
00957       for (int i = 0; i < 2; ++i)
00958       {
00959         for (int j = i; j < 2; ++j)
00960         {
00961           sd.push_back(covariance(i,j));
00962           disp_stream << covariance(i,j) << " ";
00963         }
00964       }
00965       // ROS_INFO_STREAM("Covariance: " << disp_stream.str());
00966     }
00967     else
00968     {
00969       ROS_WARN_STREAM("Failed to get covariance matrix");
00970       for (int i = 0; i < 3; ++i)
00971       {
00972         sd.push_back(0);
00973       }
00974     }
00975   }
00976   else
00977   {
00978     ROS_WARN_STREAM("Failed to get centroid");
00979     for (int i = 0; i < 3; ++i)
00980     {
00981       sd.push_back(0);
00982     }
00983   }
00984 }
00985 
00986 void extractPCAFeaturesXY(XYZPointCloud& samples, ShapeDescriptor& sd)
00987 {
00988   pcl16::PCA<pcl16::PointXYZ> pca;
00989   pca.setInputCloud(samples.makeShared());
00990   Eigen::Vector3f eigen_values = pca.getEigenValues();
00991   Eigen::Matrix3f eigen_vectors = pca.getEigenVectors();
00992   Eigen::Vector4f centroid = pca.getMean();
00993   double lambda0 = eigen_values(0);
00994   double lambda1 = eigen_values(1);
00995   // ROS_INFO_STREAM("Lambda: " << lambda0 << ", " << lambda1 << ", " << eigen_values(2));
00996   // ROS_INFO_STREAM("lambda0/lambda1: " << lambda0/lambda1);
00997   // ROS_INFO_STREAM("Eigen vectors: \n" << eigen_vectors);
00998   // Get inertia of points
00999   sd.push_back(lambda0);
01000   sd.push_back(lambda1);
01001   sd.push_back(lambda0/lambda1);
01002   // Get angls of eigen vectors
01003   double theta0 = atan2(eigen_vectors(1,0), eigen_vectors(0,0));
01004   double theta1 = atan2(eigen_vectors(1,1), eigen_vectors(0,1));
01005   // ROS_INFO_STREAM("theta: " << theta0 << ", " << theta1);
01006   sd.push_back(theta0);
01007   sd.push_back(theta1);
01008 }
01009 
01010 void extractBoundingBoxFeatures(XYZPointCloud& samples, ShapeDescriptor& sd)
01011 {
01012   std::vector<cv::Point2f> obj_pts;
01013   for (unsigned int i = 0; i < samples.size(); ++i)
01014   {
01015     obj_pts.push_back(cv::Point2f(samples[i].x, samples[i].y));
01016   }
01017   cv::RotatedRect box = cv::minAreaRect(obj_pts);
01018   double l = std::max(box.size.width, box.size.height);
01019   double w = std::min(box.size.width, box.size.height);
01020   // ROS_INFO_STREAM("(l,w): " << l << ", " << w << ") l/w: " << l/w);
01021   sd.push_back(l);
01022   sd.push_back(w);
01023   sd.push_back(l/w);
01024 }
01025 
01026 ShapeDescriptor extractLocalShapeFeatures(XYZPointCloud& hull, ProtoObject& cur_obj,
01027                                           pcl16::PointXYZ sample_pt, double sample_spread, double hull_alpha,
01028                                           double hist_res)
01029 {
01030   XYZPointCloud local_samples = getLocalSamples(hull, cur_obj, sample_pt, sample_spread, hull_alpha);
01031   // Transform points into sample_pt frame
01032   XYZPointCloud transformed_pts = transformSamplesIntoSampleLocFrame(local_samples, cur_obj, sample_pt);
01033 
01034   // Compute features and populate the descriptor
01035   ShapeDescriptor sd;
01036 #ifdef USE_RANGE_AND_VAR_FEATS
01037   getPointRangesXY(transformed_pts, sd);
01038   getCovarianceXYFromPoints(transformed_pts, sd);
01039   extractPCAFeaturesXY(transformed_pts, sd);
01040   extractBoundingBoxFeatures(transformed_pts, sd);
01041 #endif // USE_RANGE_AND_VAR_FEATS
01042 
01043   // Get histogram to describe local distribution
01044   ShapeDescriptor histogram = extractPointHistogramXY(transformed_pts, hist_res, hist_res,
01045                                                       sample_spread*2, sample_spread*2);
01046   sd.insert(sd.end(), histogram.begin(), histogram.end());
01047   return sd;
01048 }
01049 
01050 
01051 
01052 ShapeDescriptor extractGlobalShapeFeatures(XYZPointCloud& hull, ProtoObject& cur_obj, pcl16::PointXYZ sample_pt,
01053                                            int sample_pt_idx, double sample_spread)
01054 {
01055   XYZPointCloud transformed_pts = transformSamplesIntoSampleLocFrame(cur_obj.cloud, cur_obj, sample_pt);
01056   ShapeDescriptor sd;
01057 #ifdef USE_RANGE_AND_VAR_FEATS
01058   // Get the general features describing the point cloud in the local object frame
01059   getPointRangesXY(transformed_pts, sd);
01060   getCovarianceXYFromPoints(transformed_pts, sd);
01061   extractPCAFeaturesXY(transformed_pts, sd);
01062   extractBoundingBoxFeatures(transformed_pts, sd);
01063 #endif // USE_RANGE_AND_VAR_FEATS
01064 
01065   // Get shape context
01066   ShapeLocations sc = extractShapeContextFromSamples(hull, cur_obj, true);
01067   ShapeDescriptor histogram = sc[sample_pt_idx].descriptor_;
01068   sd.insert(sd.end(), histogram.begin(), histogram.end());
01069   std::stringstream descriptor;
01070   descriptor << "";
01071   for (unsigned int i = 0; i < histogram.size(); ++i)
01072   {
01073     if (i % 5 == 0)
01074     {
01075       descriptor << "\n";
01076     }
01077     descriptor << " " << histogram[i];
01078   }
01079   // ROS_INFO_STREAM("Shape context: " << descriptor.str());
01080 
01081 
01082   return sd;
01083 }
01084 
01085 ShapeDescriptors extractLocalAndGlobalShapeFeatures(XYZPointCloud& hull, ProtoObject& cur_obj,
01086                                                     double sample_spread, double hull_alpha,
01087                                                     double hist_res)
01088 {
01089   ShapeDescriptors descs;
01090   for (unsigned int i = 0; i < hull.size(); ++i)
01091   {
01092     ShapeDescriptor d = extractLocalAndGlobalShapeFeatures(hull, cur_obj, hull[i], i, sample_spread, hull_alpha,
01093                                                            hist_res);
01094     descs.push_back(d);
01095   }
01096   return descs;
01097 }
01098 
01099 ShapeDescriptor extractLocalAndGlobalShapeFeatures(XYZPointCloud& hull, ProtoObject& cur_obj,
01100                                                    pcl16::PointXYZ sample_pt, int sample_pt_idx,
01101                                                    double sample_spread, double hull_alpha, double hist_res)
01102 {
01103   // ROS_INFO_STREAM("Local");
01104   ShapeDescriptor local_raw = extractLocalShapeFeatures(hull, cur_obj, sample_pt, sample_spread, hull_alpha, hist_res);
01105   // Binarize local histogram
01106   for (unsigned int i = 0; i < local_raw.size(); ++i)
01107   {
01108     if (local_raw[i] > 0)
01109     {
01110       local_raw[i] = 1.0;
01111     }
01112     else
01113     {
01114       local_raw[i] = 0.0;
01115     }
01116   }
01117   // Convert back into image for resizing
01118   int hist_size = std::sqrt(local_raw.size());
01119   cv::Mat local_hist(cv::Size(hist_size, hist_size), CV_64FC1, cv::Scalar(0.0));
01120   std::stringstream raw_hist;
01121   for (int r = 0; r < hist_size; ++r)
01122   {
01123     for (int c = 0; c < hist_size; ++c)
01124     {
01125       local_hist.at<double>(r,c) = local_raw[r*hist_size+c];
01126       raw_hist << " " << local_hist.at<double>(r,c);
01127     }
01128     raw_hist << "\n";
01129   }
01130   // Resize to 6x6
01131   // TODO: Set 6 as a variable
01132   cv::Mat local_resize(cv::Size(6,6), CV_64FC1, cv::Scalar(0.0));
01133   cpl_visual_features::imResize(local_hist, 6./hist_size, local_resize);
01134   std::stringstream resized_hist;
01135   for (int r = 0; r < local_resize.rows; ++r)
01136   {
01137     for (int c = 0; c < local_resize.cols; ++c)
01138     {
01139       resized_hist << " " << local_resize.at<double>(r,c);
01140     }
01141     resized_hist << "\n";
01142   }
01143   // TODO: Compare the resized histogram to computing 6x6 directly
01144   // Filter with gaussian
01145   cv::Mat local_smooth(local_resize.size(), CV_64FC1, cv::Scalar(0.0));
01146   cv::Mat g_kernel = cv::getGaussianKernel(5, 0.2, CV_64F);
01147   cv::sepFilter2D(local_resize, local_smooth, CV_64F, g_kernel, g_kernel);
01148   // Threshold negatives then L1 normalize
01149   double local_sum = 0.0;
01150   std::stringstream smooth_hist;
01151   std::stringstream smooth_hist_clip;
01152   for (int r = 0; r < local_smooth.rows; ++r)
01153   {
01154     for (int c = 0; c < local_smooth.cols; ++c)
01155     {
01156       smooth_hist << " " << local_smooth.at<double>(r,c);
01157       if(local_smooth.at<double>(r,c) < 0)
01158       {
01159         local_smooth.at<double>(r,c) = 0.0;
01160       }
01161       else
01162       {
01163         local_sum += local_smooth.at<double>(r,c);
01164       }
01165       smooth_hist_clip << " " << local_smooth.at<double>(r,c);
01166     }
01167     smooth_hist << "\n";
01168     smooth_hist_clip << "\n";
01169   }
01170   // L1 normalize local histogram
01171   ShapeDescriptor local;
01172   std::stringstream l1_hist;
01173   for (int r = 0; r < local_smooth.rows; ++r)
01174   {
01175     for (int c = 0; c < local_smooth.cols; ++c)
01176     {
01177       local_smooth.at<double>(r,c) /= local_sum;
01178       local.push_back(local_smooth.at<double>(r,c));
01179       l1_hist << " " << local_smooth.at<double>(r,c);
01180     }
01181     l1_hist << "\n";
01182   }
01183   // ROS_INFO_STREAM("raw:\n" << raw_hist.str());
01184   // ROS_INFO_STREAM("resized:\n" << resized_hist.str());
01185   // ROS_INFO_STREAM("smooth:\n" << smooth_hist.str());
01186   // ROS_INFO_STREAM("smooth_clip:\n" << smooth_hist_clip.str());
01187   // ROS_INFO_STREAM("l1_normed:\n" << l1_hist.str());
01188   // ROS_INFO_STREAM("Local raw size: " << local_raw.size());
01189 
01190   // ROS_INFO_STREAM("Global");
01191   ShapeDescriptor global = extractGlobalShapeFeatures(hull, cur_obj, sample_pt, sample_pt_idx, sample_spread);
01192   // Binarize global histogram then L1 normalize
01193   double global_sum = 0.0;
01194   for (unsigned int i = 0; i < global.size(); ++i)
01195   {
01196     if (global[i] > 0)
01197     {
01198       global[i] = 1.0;
01199       global_sum += 1.0;
01200     }
01201     else
01202     {
01203       global[i] = 0.0;
01204     }
01205   }
01206   // L1 normalize global histogram
01207   for (unsigned int i = 0; i < global.size(); ++i)
01208   {
01209     global[i] /= global_sum;
01210   }
01211 
01212   // ROS_INFO_STREAM("local.size() << " << local.size());
01213   // ROS_INFO_STREAM("global.size() << " << global.size());
01214   local.insert(local.end(), global.begin(), global.end());
01215   // ROS_INFO_STREAM("local.size() << " << local.size());
01216   return local;
01217 }
01218 
01219 
01227 cv::Mat computeShapeFeatureAffinityMatrix(ShapeLocations& locs, bool use_center)
01228 {
01229   cv::Mat affinity(locs.size(), locs.size(), CV_64FC1, cv::Scalar(0.0));
01230   double max_affinity = 0.0;
01231   ShapeDescriptors normalized;
01232   for (int i = 0; i < locs.size(); ++i)
01233   {
01234     ShapeDescriptor desc(locs[i].descriptor_);
01235     double feature_sum = 0;
01236     for (int j = 0; j < desc.size(); ++j)
01237     {
01238       feature_sum += desc[j];
01239     }
01240     if (feature_sum == 0) continue;
01241     for (int j = 0; j < desc.size(); ++j)
01242     {
01243       desc[j] = sqrt(desc[j]/feature_sum);
01244     }
01245     normalized.push_back(desc);
01246   }
01247 
01248   for (int r = 0; r < affinity.rows; ++r)
01249   {
01250     for (int c = r; c < affinity.cols; ++c)
01251     {
01252       if (r == c)
01253       {
01254         affinity.at<double>(r,c) = 1.0;
01255         continue;
01256       }
01257       double sim_score = 1.0 - sqrt(shapeFeatureSquaredEuclideanDist(normalized[r],normalized[c]));
01258       affinity.at<double>(r,c) = sim_score;
01259       affinity.at<double>(c,r) = sim_score;
01260       if (affinity.at<double>(r,c) > max_affinity)
01261       {
01262         max_affinity = affinity.at<double>(r,c);
01263       }
01264     }
01265   }
01266   if (use_center)
01267   {
01268     cv::imshow("affinity-with-centers", affinity);
01269   }
01270   else
01271   {
01272     cv::imshow("affinity", affinity);
01273   }
01274   return affinity;
01275 }
01276 
01286 double shapeFeatureChiSquareDist(ShapeDescriptor& a, ShapeDescriptor& b, double gamma)
01287 {
01288   // compute dist between shape features a and b
01289   // using chi-squared test statistic
01290   double chi = 0;
01291 
01292   for (unsigned int k=0; k < a.size(); k++)
01293   {
01294     const double a_plus_b = a[k] + b[k];
01295     if (a_plus_b > 0)
01296     {
01297       chi += pow(a[k] - b[k], 2) / (a_plus_b);
01298     }
01299   }
01300   chi = chi;
01301   if (gamma != 0.0)
01302   {
01303     chi = exp(-gamma*chi);
01304   }
01305   return chi;
01306 }
01307 
01316 double shapeFeatureSquaredEuclideanDist(ShapeDescriptor& a, ShapeDescriptor& b)
01317 {
01318   double dist = 0;
01319   for(int k = 0; k < a.size(); ++k)
01320   {
01321     double k_dist = a[k] - b[k];
01322     dist += k_dist*k_dist;
01323   }
01324   return dist;
01325 }
01326 
01327 void clusterShapeFeatures(ShapeLocations& locs, int num_clusters, std::vector<int>& cluster_ids, ShapeDescriptors& centers,
01328                           double min_err_change, int max_iter, int num_retries)
01329 {
01330   cv::Mat samples(locs.size(), locs[0].descriptor_.size(), CV_64FC1);
01331   for (int r = 0; r < samples.rows; ++r)
01332   {
01333     // NOTE: Normalize features here
01334     double feature_sum = 0;
01335     for (int c = 0; c < samples.cols; ++c)
01336     {
01337       samples.at<double>(r,c) = locs[r].descriptor_[c];
01338       feature_sum += samples.at<double>(r,c);
01339     }
01340     if (feature_sum == 0)
01341     {
01342       continue;
01343     }
01344     for (int c = 0; c < samples.cols; ++c)
01345     {
01346       samples.at<double>(r,c) /= feature_sum;
01347       // NOTE: Use Hellinger distance for comparison
01348       samples.at<double>(r,c) = sqrt(samples.at<double>(r,c));
01349     }
01350   }
01351   cv::TermCriteria term_crit(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, max_iter, min_err_change);
01352   // cv::Mat labels;
01353   cv::Mat centers_cv;
01354   double slack = cv::kmeans(samples, num_clusters, cluster_ids, term_crit, num_retries, cv::KMEANS_PP_CENTERS,
01355                             centers_cv);
01356   for (int r = 0; r < centers_cv.rows; ++r)
01357   {
01358     ShapeDescriptor s(centers_cv.cols, 0);
01359     for (int c = 0; c < centers_cv.cols; ++c)
01360     {
01361       s[c] = centers_cv.at<double>(r,c);
01362     }
01363     centers.push_back(s);
01364   }
01365 }
01366 
01375 int closestShapeFeatureCluster(ShapeDescriptor& descriptor, ShapeDescriptors& centers, double& min_dist)
01376 {
01377   int min_idx = -1;
01378   min_dist = FLT_MAX;
01379   ShapeDescriptor normalized(descriptor);
01380   double feature_sum = 0;
01381   for (int i = 0; i < normalized.size(); ++i)
01382   {
01383     feature_sum += normalized[i];
01384   }
01385   for (int i = 0; i < normalized.size(); ++i)
01386   {
01387     normalized[i] = sqrt(normalized[i]/feature_sum);
01388   }
01389   for (int c = 0; c < centers.size(); ++c)
01390   {
01391     double c_dist = (shapeFeatureSquaredEuclideanDist(normalized, centers[c]));
01392     if (c_dist < min_dist)
01393     {
01394       min_dist = c_dist;
01395       min_idx = c;
01396     }
01397   }
01398   return min_idx;
01399 }
01400 
01401 ShapeDescriptors loadSVRTrainingFeatures(std::string feature_path, int feat_length)
01402 {
01403   std::ifstream data_in(feature_path.c_str());
01404   ShapeDescriptors train_feats;
01405   while (data_in.good())
01406   {
01407     ShapeDescriptor feat(feat_length, 0.0);
01408     char c_line[4096];
01409     data_in.getline(c_line, 4096);
01410     std::stringstream line;
01411     line << c_line;
01412     int idx;
01413     double val;
01414     int num_feats = 0;
01415     // std::stringstream debug_display;
01416     while (line >> idx)
01417     {
01418       if (line.peek() == ':')
01419       {
01420         line.ignore();
01421         line >> val;
01422         feat[idx-1] = val;
01423         num_feats++;
01424         // debug_display << "[" << idx-1 << "] = " << val << " ";
01425       }
01426       if (line.peek() == ' ')
01427       {
01428         line.ignore();
01429       }
01430     }
01431     // ROS_INFO_STREAM(debug_display.str());
01432     if (num_feats > 0)
01433     {
01434       train_feats.push_back(feat);
01435     }
01436   }
01437   data_in.close();
01438   return train_feats;
01439 }
01440 
01441 cv::Mat computeChi2Kernel(ShapeDescriptors& sds, std::string feat_path, int local_length, int global_length,
01442                           double gamma_local, double gamma_global, double mixture_weight)
01443 {
01444   ShapeDescriptors train_feats = loadSVRTrainingFeatures(feat_path, local_length + global_length);
01445   cv::Mat K_local(train_feats.size(), sds.size(), CV_64FC1, cv::Scalar(0.0));
01446   cv::Mat K_global(train_feats.size(), sds.size(), CV_64FC1, cv::Scalar(0.0));
01447   for (int i = 0; i < sds.size(); ++i)
01448   {
01449     ShapeDescriptor a_local(sds[i].begin(), sds[i].begin()+local_length);
01450     ShapeDescriptor a_global(sds[i].begin()+local_length, sds[i].end());
01451     for (int j = 0; j < train_feats.size(); ++j)
01452     {
01453       ShapeDescriptor b_local(train_feats[j].begin(), train_feats[j].begin()+local_length);
01454       ShapeDescriptor b_global(train_feats[j].begin()+local_length, train_feats[j].end());
01455 
01456       K_local.at<double>(j, i) = shapeFeatureChiSquareDist(a_local, b_local, gamma_local);
01457       K_global.at<double>(j, i) = shapeFeatureChiSquareDist(a_global, b_global, gamma_global);
01458     }
01459   }
01460   // Linear combination of local and global kernels
01461   cv::Mat K = mixture_weight * K_global + (1 - mixture_weight) * K_local;
01462   return K;
01463 }
01464 
01465 };


tabletop_pushing
Author(s): Tucker Hermans
autogenerated on Wed Nov 27 2013 11:59:44