int main (int argc, char* argv[])
{
  boost::program_options::options_description desc("Options");
  desc.add_options()
    ("help", "Print help message")
    ("pcd_filename", boost::program_options::value<std::string>()->required(), "pcl pcd filename");

  boost::program_options::variables_map vm;
  try {
    boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm);
    if( vm.count("help") ){
      std::cout << "Resize Point cloud using voxel grid filter" << std::endl;
      std::cerr << desc << std::endl;
      return 0;
    }
    boost::program_options::notify(vm);
  } catch (boost::program_options::error& e) {
    std::cerr << "ERROR: " << e.what() << std::endl << std::endl;
    std::cerr << desc << std::endl;
    return -1;
  }

  std::string pcd_fname = vm["pcd_filename"].as<std::string>();
  pcl::PointCloud<pcl::PointXYZRGB>::Ptr source_cloud (new pcl::PointCloud<pcl::PointXYZRGB>);
  pcl::io::loadPCDFile (pcd_fname, *source_cloud);

  // Rotate pointcloud 0.6[rad] around z axis
  Eigen::Matrix4f rotate_around_zaxis = Eigen::Matrix4f::Identity();
  float theta = 0.6;
  rotate_around_zaxis(0,0) = cos(theta);
  rotate_around_zaxis(0,1) = -sin(theta);
  rotate_around_zaxis(1,0) = sin(theta);
  rotate_around_zaxis(1,1) = cos(theta);
  pcl::PointCloud<pcl::PointXYZRGB>::Ptr rotated_cloud(new pcl::PointCloud<pcl::PointXYZRGB> ());
  pcl::transformPointCloud(*source_cloud, *rotated_cloud, rotate_around_zaxis);

  // Transform point cloud using homogeneous transformation matrix
  // which is got by icp mathcing between 3D map reference point and manual map reference point
  Eigen::Matrix4f icp_transform_matrix = Eigen::Matrix4f::Identity();
  icp_transform_matrix(0, 0) = 0.950235;
  icp_transform_matrix(0, 1) = -0.307961;
  icp_transform_matrix(0, 2) = 0.0470266;
  icp_transform_matrix(0, 3) = 0.147511;
  icp_transform_matrix(1, 0) = 0.30832;
  icp_transform_matrix(1, 1) = 0.951281;
  icp_transform_matrix(1, 2) = -0.000411891;
  icp_transform_matrix(1, 3) = -0.0305933;
  icp_transform_matrix(2, 0) = -0.0446088;
  icp_transform_matrix(2, 1) = 0.0148907;
  icp_transform_matrix(2, 2) = 0.998893;
  icp_transform_matrix(2, 3) = -0.0312964;
  pcl::PointCloud<pcl::PointXYZRGB>::Ptr transformed_cloud(new pcl::PointCloud<pcl::PointXYZRGB> ());
  pcl::transformPointCloud(*rotated_cloud, *transformed_cloud, icp_transform_matrix);

  pcl::io::savePCDFileASCII("transformed_cloud.pcd", *transformed_cloud);
  std::cout << "Write to transformed_cloud.pcd" << std::endl;
  return 0;
}
template <typename PointT> typename open_ptrack::detection::GroundBasedPeopleDetectionApp<PointT>::PointCloudPtr
open_ptrack::detection::GroundBasedPeopleDetectionApp<PointT>::rotateCloud(PointCloudPtr cloud, Eigen::Affine3f transform )
{
  PointCloudPtr rotated_cloud (new PointCloud);
  pcl::transformPointCloud(*cloud, *rotated_cloud, transform);
  rotated_cloud->header.frame_id = cloud->header.frame_id;

  return rotated_cloud;

}
// I need to change the parameters to account for the
// transformation matrices. I need to save the best TM
// so I can combine it with the icp TM.
//
// Does kdtree search on each rotation of 45 degrees around
// the central point of the moved_cloud
Eigen::Matrix4f kdtree_search(pcl::PointCloud<pcl::PointXYZ>::Ptr input_cloud, pcl::PointCloud<pcl::PointXYZ>::Ptr moved_cloud, float radius)
{
  pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;
  std::vector<int> pointIdxRadiusSearch;
  std::vector<float> pointRadiusSquaredDistance;

  // Total number of nearest neighbors
  int num_matches = 0;
  // Return the cloud with the most number of nearest neighbors
  // within a given radius of the searchPoint
  int max_neighbors = 0;
  pcl::PointCloud<pcl::PointXYZ>::Ptr best_fit_cloud ;
  Eigen::Matrix4f best_fit_transform = Eigen::Matrix4f::Identity();

  Eigen::Vector4f centroid1 = compute_centroid(*moved_cloud);
  cout << "centroid of source cloud - " << centroid1(0)
  << ", " << centroid1(1) << ", " << centroid1(2) << endl;

  // Executing the transformation
  pcl::PointCloud<pcl::PointXYZ>::Ptr rotated_cloud (new pcl::PointCloud<pcl::PointXYZ> ());
  pcl::PointCloud<pcl::PointXYZ>::Ptr transformed_cloud (new pcl::PointCloud<pcl::PointXYZ> ());

  Eigen::Matrix4f transform_1 = Eigen::Matrix4f::Identity();
  Eigen::Matrix4f transform_2 = Eigen::Matrix4f::Identity();

  for (int i = 0; i < 8; i++)
  {
    float theta = 45 * i + 45;
    transform_1 (0,0) = cos (theta);
    transform_1 (0,2) = -sin (theta);
    transform_1 (2,0) = sin (theta);
    transform_1 (2,2) = cos (theta);
    cout << "cloud with " << theta << " degrees of rotation" << endl;
pcl::transformPointCloud (*moved_cloud, *rotated_cloud, transform_1);

// Probably need to compute centroid of the new transformed cloud
// because the transformation seems to translate it
  Eigen::Vector4f centroid2 = compute_centroid(*rotated_cloud);
  cout << "centroid of rotated cloud - " << centroid2(0)
  << ", " << centroid2(1) << ", " << centroid2(2) << endl;
  float distance_x = centroid1(0) - centroid2(0);
  float distance_y = centroid1(1) - centroid2(1);
  float distance_z = centroid1(2) - centroid2(2);
  cout << "distance between centroids: (" << distance_x << ", " << distance_y << ", " << distance_z << ")" << endl;
  transform_2 (0,3) = (distance_x);
  transform_2 (1,3) = (distance_y);
  transform_2 (2,3) = (distance_z);
pcl::transformPointCloud (*rotated_cloud, *transformed_cloud, transform_2);


    // Rotate the cloud by 45 degrees each time
    // May want to add some random translation as well.
    // This would correspond to doing kdtree search on a number of
    // clouds that are presumably close to the target point cloud.
    kdtree.setInputCloud(transformed_cloud);

    // Run the kdtree search on every 10th point of the moved_cloud
    // Increase this number to speed up the search
    // Test with different increments of i to see effect on speed
    for (int j = 0; j < (*moved_cloud).points.size(); j += 10)
    {
      pcl::PointXYZ searchPoint = moved_cloud->points[j];
      int num_neighbors = kdtree.radiusSearch(searchPoint, radius, pointIdxRadiusSearch, pointRadiusSquaredDistance);
      num_matches += num_neighbors;
      cout << "performed kdtree nearest neighbor search, found " << num_neighbors << " within " << radius << " radius" << endl;
    }
    cout << "num_matches = " << num_matches << endl;
    cout << "max_neighbors = " << max_neighbors << endl;

    if (num_matches > max_neighbors) {
      max_neighbors = num_matches;
      best_fit_cloud = transformed_cloud;
      // are these transforms relative? or absolute?
      // this currently calculates relative
      // should be transform_2 * transform_1 if absolute
      best_fit_transform = transform_2 * transform_1;
     }
    num_matches = 0;
  printf(  "\nPoint cloud colors :  white  = original point cloud\n"
      "                        red  = transformed point cloud\n"
      "                        blue = moved point cloud\n"
      "                        green = rotated point cloud\n");
  pcl::visualization::PCLVisualizer viewer ("Matrix transformation example");

   // Define R,G,B colors for the point cloud
//  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> source_cloud_color_handler (source_cloud, 255, 255, 255);  // white
  // We add the point cloud to the viewer and pass the color handler
//  viewer.addPointCloud (source_cloud, source_cloud_color_handler, "original_cloud");


  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> rotated_cloud_color_handler (rotated_cloud, 20, 245, 20); // green
  viewer.addPointCloud (rotated_cloud, rotated_cloud_color_handler, "rotated_cloud");


  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> transformed_cloud_color_handler (transformed_cloud, 230, 20, 20); // Red
  viewer.addPointCloud (transformed_cloud, transformed_cloud_color_handler, "transformed_cloud");

  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> moved_cloud_color_handler (moved_cloud, 20, 230, 230); // blue
  viewer.addPointCloud (moved_cloud, moved_cloud_color_handler, "moved_cloud");

  viewer.addCoordinateSystem (1.0, 0);
  viewer.setBackgroundColor(0.05, 0.05, 0.05, 0); // Setting background to a dark grey
//  viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "original_cloud");
  viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "transformed_cloud");
  viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "rotated_cloud");
  viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "moved_cloud");
  //viewer.setPosition(800, 400); // Setting visualiser window position
  while (!viewer.wasStopped ()) { // Display the visualiser until 'q' key is pressed
    viewer.spinOnce ();
  }

  }
  // check whether the translations are relative or absolute
  pcl::PointCloud<pcl::PointXYZ>::Ptr best_transform_cloud (new pcl::PointCloud<pcl::PointXYZ> ());
  pcl::transformPointCloud (*moved_cloud, *best_transform_cloud, best_fit_transform);

  pcl::visualization::PCLVisualizer viewer ("Matrix transformation example");

  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> best_transform_cloud_color_handler (best_transform_cloud, 245, 20, 20); // red
  viewer.addPointCloud (best_transform_cloud, best_transform_cloud_color_handler, "best_transform_cloud");

  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> best_fit_cloud_color_handler (best_fit_cloud, 20, 245, 20); // green
  viewer.addPointCloud (best_transform_cloud, best_fit_cloud_color_handler, "best_fit_cloud");

  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> input_cloud_color_handler (input_cloud, 255, 255, 255); // white
  viewer.addPointCloud (input_cloud, input_cloud_color_handler, "input_cloud");

  viewer.addCoordinateSystem (1.0, 0);
  viewer.setBackgroundColor(0.05, 0.05, 0.05, 0); // Setting background to a dark grey

  viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "best_transform_cloud");
  viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "best_fit_cloud");
  viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "input_cloud");
  while (!viewer.wasStopped ()) { // Display the visualiser until 'q' key is pressed
    viewer.spinOnce ();
  }
  return best_fit_transform;
}