frame_extended.h
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2010, Willow Garage, Inc.
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of Willow Garage, Inc. nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *
00034  * $Id: frame_extended.h 43557 2010-08-21 00:16:14Z mihelich $
00035  *
00036  */
00037 
00038 #ifndef _FRAME_EXTENDED_H_
00039 #define _FRAME_EXTENDED_H_
00040 
00041 #include "frame_common/frame.h"
00042 // PCL header
00043 #include <pcl/point_types.h>
00044 #include <pcl/point_cloud.h>
00045 #include <pcl/PointIndices.h>
00046 #include <pcl/features/normal_3d.h>
00047 #include <pcl/kdtree/kdtree_flann.h>
00048 #include <pcl/filters/passthrough.h>
00049 #include <pcl/filters/voxel_grid.h>
00050 #include <pcl/filters/filter.h>
00051 #include <pcl/filters/extract_indices.h>
00052 #include <pcl/common/transforms.h>
00053 #include <pcl/registration/transforms.h>
00054 #include <pcl/registration/icp.h>
00055 #include <pcl/registration/icp_nl.h>
00056 
00057 #include <pcl/io/pcd_io.h>
00058 
00059 using namespace pcl;
00060 using namespace Eigen;
00061 
00062 namespace frame_common
00063 {
00065   class FrameExtended : public Frame
00066   {
00067     public:
00069       pcl::PointCloud<pcl::PointXYZRGBNormal> cloud;
00071       pcl::PointIndices indices;
00072 
00074       template <typename PointT, typename PointNT> void
00075         estimateNormals (const pcl::PointCloud<PointT> &input, const pcl::PointIndices &indices, pcl::PointCloud<PointNT> &output)
00076       {
00077         // Create an object for spatial search (e.g., nearest neighbor estimation)
00078         pcl::search::OrganizedNeighbor<PointT> tree;
00079 
00080         // Create a normal estimation object
00081         pcl::NormalEstimation<PointT, PointNT> ne;
00082         ne.setInputCloud (boost::make_shared<const pcl::PointCloud<PointT> > (input));      // pass the data
00083         ne.setIndices (boost::make_shared<std::vector<int> > (indices.indices));            // pass the indices
00084         ne.setSearchMethod (tree);                                                          // pass the spatial search
00085         // Use 10 nearest neighbors to estimate the normals
00086         ne.setKSearch (10);
00087 
00088         ne.compute (output);
00089       }
00090       
00092       void match(frame_common::FrameExtended& frame, const Eigen::Vector3d& trans, const Eigen::Quaterniond& rot, std::vector<cv::DMatch>& matches)
00093       {
00094         PointCloud<PointXYZRGBNormal> transformed_cloud;
00095         
00096         // First, transform the current frame. (Is this inverse?) (Or just transform the other cloud?)
00097         transformPointCloudWithNormals<PointXYZRGBNormal>(frame.cloud, transformed_cloud, -trans.cast<float>(), rot.cast<float>().conjugate());
00098         
00099         Matrix3d rotmat = rot.toRotationMatrix();
00100         
00101         // Optional/TODO: Perform ICP to further refine estimate.
00102         /*PointCloud<PointXYZRGBNormal> cloud_reg;
00103 
00104         IterativeClosestPointNonLinear<PointXYZRGBNormal, PointXYZRGBNormal> reg;
00105         reg.setInputCloud (boost::make_shared<const PointCloud<PointXYZRGBNormal> > (transformed_cloud));
00106         reg.setInputTarget (boost::make_shared<const PointCloud<PointXYZRGBNormal> > (cloud));
00107         reg.setMaximumIterations(50);
00108         reg.setTransformationEpsilon (1e-8);
00109 
00110         reg.align(cloud_reg); */
00111               
00112         // Find matches between pointclouds in frames. (TODO: also compare normals)
00113         std::vector<int> other_indices, this_indices;
00114         getMatchingIndices(transformed_cloud, cloud, other_indices, this_indices);
00115         
00116         // Fill in keypoints and projections of relevant features.
00117         // Currently just done when setting the pointcloud.
00118         
00119         // Convert matches into the correct format.
00120         matches.clear();
00121         // Starting at i=1 as a hack to not let through (0,0,0) matches (why is this in the ptcloud?))
00122         for (unsigned int i=1; i < other_indices.size(); i++)
00123         {           
00124           PointXYZRGBNormal &pt0 = transformed_cloud.points[other_indices[i]];
00125           PointXYZRGBNormal &pt1 = cloud.points[this_indices[i]];
00126           
00127           // Figure out distance and angle between normals
00128           Quaterniond normalquat;
00129           Vector3d norm0(pt0.normal[0], pt0.normal[1], pt0.normal[2]), norm1(pt1.normal[0], pt1.normal[1], pt1.normal[2]);
00130           normalquat.setFromTwoVectors(norm0, norm1);
00131           double angledist = normalquat.angularDistance(normalquat.Identity());
00132           double dist = (Vector3d(pt0.x, pt0.y, pt0.z)-Vector3d(pt1.x, pt1.y, pt1.z)).norm();
00133           
00134           Vector4d p0_pt = Vector4d(pt0.x, pt0.y, pt0.z, 1.0);
00135           Vector3d expected_proj = projectPoint(p0_pt);
00136           
00137           Vector3d diff = expected_proj - pl_kpts[this_indices[i]].head<3>();
00138           diff(2) = diff(2) - diff(0);
00139           
00140           if ((norm0 - norm1).norm() < 0.5)
00141             matches.push_back(cv::DMatch(other_indices[i], this_indices[i], dist));
00142         }
00143         
00144         printf("[FrameExtended] Found %d matches, then converted %d matches.\n", (int)other_indices.size(), (int)matches.size());
00145       }
00146       
00149       void setPointcloud(const pcl::PointCloud<pcl::PointXYZRGB>& input_cloud)
00150       {
00151         reduceCloud(input_cloud, cloud);
00152         
00153         // For now, let's keep a 1-1 mapping between pl_pts, keypts, etc., etc.
00154         // Basically replicating all the info in the pointcloud but whatever.
00155         // TODO: Do something more intelligent than this.
00156         pl_pts.clear();
00157         pl_kpts.clear();
00158         pl_normals.clear();
00159         pl_ipts.clear();
00160         
00161         unsigned int ptcloudsize = cloud.points.size();
00162         pl_pts.resize(ptcloudsize);
00163         pl_kpts.resize(ptcloudsize);
00164         pl_normals.resize(ptcloudsize);
00165         pl_ipts.resize(ptcloudsize);
00166         
00167         for (unsigned int i=0; i < cloud.points.size(); i++)
00168         {
00169           PointXYZRGBNormal &pt = cloud.points[i];
00170           
00171           pl_pts[i] = Eigen::Vector4d(pt.x, pt.y, pt.z, 1.0);
00172           pl_normals[i] = Eigen::Vector4d(pt.normal[0], pt.normal[1], pt.normal[2], 1.0);
00173           pl_kpts[i] = projectPoint(pl_pts[i]);
00174           pl_ipts[i] = -1;
00175         }
00176       }
00177       
00178     private:
00179       void reduceCloud(const PointCloud<PointXYZRGB>& input, PointCloud<PointXYZRGBNormal>& output)
00180       {
00181         PointCloud<PointXYZRGB> cloud_nan_filtered, cloud_box_filtered, cloud_voxel_reduced;
00182         PointCloud<Normal> normals;
00183         PointCloud<PointXYZRGBNormal> cloud_normals;
00184         
00185         std::vector<int> indices;
00186         
00187         // Filter out nans.
00188         removeNaNFromPointCloud(input, cloud_nan_filtered, indices);
00189         indices.clear();
00190         
00191         // Filter out everything outside a [200x200x200] box.
00192         Eigen::Vector4f min_pt(-100, -100, -100, -100);
00193         Eigen::Vector4f max_pt(100, 100, 100, 100);
00194         getPointsInBox(cloud_nan_filtered, min_pt, max_pt, indices);
00195         
00196         ExtractIndices<PointXYZRGB> boxfilter;
00197         boxfilter.setInputCloud(boost::make_shared<const PointCloud<PointXYZRGB> >(cloud_nan_filtered));
00198         boxfilter.setIndices (boost::make_shared<std::vector<int> > (indices));
00199         boxfilter.filter(cloud_box_filtered);
00200         
00201         // Reduce pointcloud
00202         VoxelGrid<PointXYZRGB> voxelfilter;
00203         voxelfilter.setInputCloud (boost::make_shared<const PointCloud<PointXYZRGB> > (cloud_box_filtered));
00204         voxelfilter.setLeafSize (0.05, 0.05, 0.05);
00205         voxelfilter.filter (cloud_voxel_reduced);
00206         
00207         // Compute normals
00208         NormalEstimation<PointXYZRGB, Normal> normalest;
00209         normalest.setViewPoint(0, 0, 0);
00210         normalest.setSearchMethod (boost::make_shared<search::KdTree<PointXYZRGB> > ());
00211         //normalest.setKSearch (10);
00212         normalest.setRadiusSearch (0.25);
00213         normalest.setInputCloud(boost::make_shared<const PointCloud<PointXYZRGB> >(cloud_voxel_reduced));
00214         normalest.compute(normals);
00215         
00216         pcl::concatenateFields (cloud_voxel_reduced, normals, cloud_normals);
00217 
00218         // Filter based on curvature
00219         PassThrough<PointXYZRGBNormal> normalfilter;
00220         normalfilter.setFilterFieldName("curvature");
00221         normalfilter.setFilterLimits(0.0, 0.1);
00222         normalfilter.setInputCloud(boost::make_shared<const PointCloud<PointXYZRGBNormal> >(cloud_normals));
00223         normalfilter.filter(output);
00224       }
00225       
00226       void getMatchingIndices(PointCloud<PointXYZRGBNormal>& input, PointCloud<PointXYZRGBNormal>& output, 
00227                 std::vector<int>& input_indices, std::vector<int>& output_indices)
00228       {
00229         // TODO: Don't calculate the KDTree each time.
00230         KdTreeFLANN<PointXYZRGBNormal> input_tree, output_tree;
00231           
00232         input_tree.setInputCloud(boost::make_shared<const PointCloud<PointXYZRGBNormal> >(input));
00233         output_tree.setInputCloud(boost::make_shared<const PointCloud<PointXYZRGBNormal> >(output));
00234         
00235         // Iterate over the output tree looking for all the input points and finding
00236         // nearest neighbors.
00237         for (unsigned int i = 0; i < input.points.size(); i++)
00238         {
00239           PointXYZRGBNormal input_pt = input.points[i];
00240           std::vector<int> input_indexvect(1), output_indexvect(1); // Create a vector of size 1.
00241           std::vector<float> input_distvect(1), output_distvect(1);
00242           
00243           // Find the nearest neighbor of the input point in the output tree.
00244           output_tree.nearestKSearch(input_pt, 1, input_indexvect, input_distvect);
00245           
00246           PointXYZRGBNormal output_pt = output.points[input_indexvect[0]];
00247           
00248           // Find the nearest neighbor of the output point in the input tree.
00249           input_tree.nearestKSearch(output_pt, 1, output_indexvect, output_distvect);
00250           
00251           // If they match, add them to the match vectors.
00252           if (output_indexvect[0] == (int)i)
00253           {
00254             input_indices.push_back(i);
00255             output_indices.push_back(input_indexvect[0]);
00256           }
00257         }
00258       }
00259       
00260       Eigen::Vector3d projectPoint(Eigen::Vector4d& point)
00261       {
00262         Eigen::Vector3d keypoint;
00263         
00264         keypoint(0) = (cam.fx*point.x()) / point.z() + cam.cx;
00265         keypoint(1) = (cam.fy*point.y()) / point.z() + cam.cy;
00266         keypoint(2) = (cam.fx*(point.x()-cam.tx)/point.z() + cam.cx);
00267         
00268         return keypoint;
00269       }
00270   };
00271   
00272   void drawVOtracks(const cv::Mat &image,
00273                   const std::vector<FrameExtended, Eigen::aligned_allocator<FrameExtended> > &frames,
00274                   cv::Mat &display);
00275 
00276 }
00277 #endif // _FRAME_EXTENDED_H_
00278 


frame_common
Author(s): Kurt Konolige
autogenerated on Thu Jan 2 2014 12:12:04