在测试阶段,随机加载一个来自于训练阶段的模型VFH特征文件(用户可以提供自己获取的模型VFH特征文件),从建立的树结构中检索相似的对象,并进行可视化显示。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef std::pair > vfh_model;
/** \brief Loads an n-D histogram file as a VFH signature
* \param path the input file name
* \param vfh the resultant VFH model
*/
bool
loadHist (const boost::filesystem::path &path, vfh_model &vfh)
{
int vfh_idx;
// Load the file as a PCD
try
{
//sensor_msgs::PointCloud2 cloud;
pcl::PCLPointCloud2 cloud;
int version;
Eigen::Vector4f origin;
Eigen::Quaternionf orientation;
pcl::PCDReader r;
int type; unsigned int idx;
r.readHeader (path.string(), cloud, origin, orientation, version, type, idx);
vfh_idx = pcl::getFieldIndex (cloud, "vfh");
if (vfh_idx == -1)
return (false);
if ((int)cloud.width * cloud.height != 1)
return (false);
}
catch (pcl::InvalidConversionException e)
{
return (false);
}
// Treat the VFH signature as a single Point Cloud
pcl::PointCloud point;
pcl::io::loadPCDFile (path.string (), point);
vfh.second.resize (308);
// std::vector fields;
std::vector fields;
getFieldIndex (point, "vfh", fields);
for (size_t i = 0; i < fields[vfh_idx].count; ++i)
{
vfh.second[i] = point.points[0].histogram[i];
}
vfh.first = path.string ();
return (true);
}
/** \brief Search for the closest k neighbors
* \param index the tree
* \param model the query model
* \param k the number of neighbors to search for
* \param indices the resultant neighbor indices
* \param distances the resultant neighbor distances
*/
inline void
nearestKSearch (flann::Index > &index, const vfh_model &model,
int k, flann::Matrix &indices, flann::Matrix &distances)
{
// Query point
flann::Matrix p = flann::Matrix(new float[model.second.size ()], 1, model.second.size ());
memcpy (&p.ptr ()[0], &model.second[0], p.cols * p.rows * sizeof (float));
indices = flann::Matrix(new int[k], 1, k);
distances = flann::Matrix(new float[k], 1, k);
index.knnSearch (p, indices, distances, k, flann::SearchParams (512));
delete[] p.ptr ();
}
/** \brief Load the list of file model names from an ASCII file
* \param models the resultant list of model name
* \param filename the input file name
*/
bool
loadFileList (std::vector &models, const std::string &filename)
{
ifstream fs;
fs.open (filename.c_str ());
if (!fs.is_open () || fs.fail ())
return (false);
std::string line;
while (!fs.eof ())
{
getline (fs, line);
if (line.empty ())
continue;
vfh_model m;
m.first = line;
models.push_back (m);
}
fs.close ();
return (true);
}
int
main (int argc, char** argv)
{
int k = 6;
double thresh = DBL_MAX; // No threshold, disabled by default
if (argc < 2)
{
pcl::console::print_error
("Need at least three parameters! Syntax is: %s [options] {kdtree.idx} {training_data.h5} {training_data.list}\n", argv[0]);
pcl::console::print_info (" where [options] are: -k = number of nearest neighbors to search for in the tree (default: ");
pcl::console::print_value ("%d", k); pcl::console::print_info (")\n");
pcl::console::print_info (" -thresh = maximum distance threshold for a model to be considered VALID (default: ");
pcl::console::print_value ("%f", thresh); pcl::console::print_info (")\n\n");
return (-1);
}
std::string extension (".pcd");
transform (extension.begin (), extension.end (), extension.begin (), (int(*)(int))tolower);
// Load the test histogram
std::vector pcd_indices = pcl::console::parse_file_extension_argument (argc, argv, ".pcd");
vfh_model histogram;
if (!loadHist (argv[pcd_indices.at (0)], histogram))
{
pcl::console::print_error ("Cannot load test file %s\n", argv[pcd_indices.at (0)]);
return (-1);
}
pcl::console::parse_argument (argc, argv, "-thresh", thresh);
// Search for the k closest matches
pcl::console::parse_argument (argc, argv, "-k", k);
pcl::console::print_highlight ("Using "); pcl::console::print_value ("%d", k); pcl::console::print_info (" nearest neighbors.\n");
std::string kdtree_idx_file_name = "kdtree.idx";
std::string training_data_h5_file_name = "training_data.h5";
std::string training_data_list_file_name = "training_data.list";
std::vector models;
flann::Matrix k_indices;
flann::Matrix k_distances;
flann::Matrix data;
// Check if the data has already been saved to disk
if (!boost::filesystem::exists ("training_data.h5") || !boost::filesystem::exists ("training_data.list"))
{
pcl::console::print_error ("Could not find training data models files %s and %s!\n",
training_data_h5_file_name.c_str (), training_data_list_file_name.c_str ());
return (-1);
}
else
{
loadFileList (models, training_data_list_file_name);
flann::load_from_file (data, training_data_h5_file_name, "training_data");
pcl::console::print_highlight ("Training data found. Loaded %d VFH models from %s/%s.\n",
(int)data.rows, training_data_h5_file_name.c_str (), training_data_list_file_name.c_str ());
}
// Check if the tree index has already been saved to disk
if (!boost::filesystem::exists (kdtree_idx_file_name))
{
pcl::console::print_error ("Could not find kd-tree index in file %s!", kdtree_idx_file_name.c_str ());
return (-1);
}
else
{
flann::Index > index (data, flann::SavedIndexParams ("kdtree.idx"));
index.buildIndex ();
nearestKSearch (index, histogram, k, k_indices, k_distances);
}
// Output the results on screen
pcl::console::print_highlight ("The closest %d neighbors for %s are:\n", k, argv[pcd_indices[0]]);
for (int i = 0; i < k; ++i)
pcl::console::print_info (" %d - %s (%d) with a distance of: %f\n",
i, models.at (k_indices[0][i]).first.c_str (), k_indices[0][i], k_distances[0][i]);
// Load the results
pcl::visualization::PCLVisualizer p (argc, argv, "VFH Cluster Classifier");
int y_s = (int)floor (sqrt ((double)k));
int x_s = y_s + (int)ceil ((k / (double)y_s) - y_s);
double x_step = (double)(1 / (double)x_s);
double y_step = (double)(1 / (double)y_s);
pcl::console::print_highlight ("Preparing to load ");
pcl::console::print_value ("%d", k);
pcl::console::print_info (" files (");
pcl::console::print_value ("%d", x_s);
pcl::console::print_info ("x");
pcl::console::print_value ("%d", y_s);
pcl::console::print_info (" / ");
pcl::console::print_value ("%f", x_step);
pcl::console::print_info ("x");
pcl::console::print_value ("%f", y_step);
pcl::console::print_info (")\n");
int viewport = 0, l = 0, m = 0;
for (int i = 0; i < k; ++i)
{
std::string cloud_name = models.at (k_indices[0][i]).first;
boost::replace_last (cloud_name, "_vfh", "");
p.createViewPort (l * x_step, m * y_step, (l + 1) * x_step, (m + 1) * y_step, viewport);
l++;
if (l >= x_s)
{
l = 0;
m++;
}
pcl::PCLPointCloud2 cloud;
pcl::console::print_highlight (stderr, "Loading "); pcl::console::print_value (stderr, "%s ", cloud_name.c_str ());
if (pcl::io::loadPCDFile (cloud_name, cloud) == -1)
break;
// Convert from blob to PointCloud
pcl::PointCloud cloud_xyz;
pcl::fromPCLPointCloud2(cloud, cloud_xyz);
if (cloud_xyz.points.size () == 0)
break;
pcl::console::print_info ("[done, ");
pcl::console::print_value ("%d", (int)cloud_xyz.points.size ());
pcl::console::print_info (" points]\n");
pcl::console::print_info ("Available dimensions: ");
pcl::console::print_value ("%s\n", pcl::getFieldsList (cloud).c_str ());
// Demean the cloud
Eigen::Vector4f centroid;
pcl::compute3DCentroid (cloud_xyz, centroid);
pcl::PointCloud::Ptr cloud_xyz_demean (new pcl::PointCloud);
pcl::demeanPointCloud (cloud_xyz, centroid, *cloud_xyz_demean);
// Add to renderer*
p.addPointCloud (cloud_xyz_demean, cloud_name, viewport);
// Check if the model found is within our inlier tolerance
std::stringstream ss;
ss << k_distances[0][i];
if (k_distances[0][i] > thresh)
{
p.addText (ss.str (), 20, 30, 1, 0, 0, ss.str (), viewport); // display the text with red
// Create a red line(创建一条红线)
pcl::PointXYZ min_p, max_p;
pcl::getMinMax3D (*cloud_xyz_demean, min_p, max_p);
std::stringstream line_name;
line_name << "line_" << i;
p.addLine (min_p, max_p, 1, 0, 0, line_name.str (), viewport);
p.setShapeRenderingProperties (pcl::visualization::PCL_VISUALIZER_LINE_WIDTH, 5, line_name.str (), viewport);
}
else
p.addText (ss.str (), 20, 30, 0, 1, 0, ss.str (), viewport);
// Increase the font size for the score*
p.setShapeRenderingProperties (pcl::visualization::PCL_VISUALIZER_FONT_SIZE, 18, ss.str (), viewport);
// Add the cluster name(添加聚类名称)
p.addText (cloud_name, 20, 10, cloud_name, viewport);
}
// Add coordianate systems to all viewports(给所有的窗口添加坐标系统)
p.addCoordinateSystem (0.1, 0);
p.spin ();
return (0);
}
选择提供的数据集之一作为测试样本,键入如下命令:
.\nearest_neighbors.exe -k 16 -thresh 50 ..\..\source\data\000.580.67\1258250240333_cluster_0_nxyz_vfh.pcd
命令表示:搜索提供的模型VFH特征文件的16个近邻模型,并将距离大于50的视为不符合用户要求的模型。
运行之后可看见可视化结果如图所示,图中将所有搜索到的近邻模型,按照距离大小从窗口最左下角开始显示所有的近邻模型,对于大于阈值的模型则用红色斜线段。
用户可以调整不同的参数k以及对应的阈值,如果调整k的话,最大值为194,即将索引树中所有模型进行排序显示,距离最小的模型与检索的模型最相似,如下图以不同的k作为测试所得到的结果,用户也可以换不同VFH特征模型进行检索。
.\nearest_neighbors.exe -k 25 -thresh 30 ..\..\source\data\401.324.52\1258078369333_cluster_0_nxyz_vfh.pcd