int
main(int argc, char **argv)
{
  pcl::PointCloud<pcl::PointXYZ>::Ptr source_cloud (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr target_cloud (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr source_cloud_registered (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::PointCloud<pcl::PointXYZ>::Ptr map_cloud (new pcl::PointCloud<pcl::PointXYZ>);
  int start_index = 1, end_index = 20;
  std::string prefix("/media/work/datasets/BRL/unorganized/scan");
  std::string source_pcd_file, target_pcd_file, source_pose_file, target_pose_file;

  map_cloud->points.clear();

//  for (int index = start_index; index < end_index; index++)
//  {
//    char buf[4];
//    doubletmp;
//    sprintf(buf, "%03d", index);
//    target_pcd_file = prefix + std::string(buf) + ".pcd";
//    target_pose_file = prefix + std::string(buf) + ".pose";
//    doubletarget_odometry[3];
//    if (pcl::io::loadPCDFile<pcl::PointXYZ> (target_pcd_file, *target_cloud) == -1) //* load the file
//    {
//      PCL_ERROR ("Couldn't read file %s!\n", target_pcd_file.c_str());
//      return (-1);
//    }
//    PCL_INFO ("Loaded %d points: (width %d, height: %d) from %s.\n",
//              target_cloud->points.size(), target_cloud->width, target_cloud->height, target_pcd_file.c_str());
//    std::ifstream target_pose_in (target_pose_file.c_str());
//    target_pose_in >> target_odometry[0] >> target_odometry[1] >> tmp >> tmp >> tmp >> target_odometry[2];
//    PCL_INFO ("target odometry (x, y, theta): (%f, %f, %f)!\n", target_odometry[0], target_odometry[1], target_odometry[2]);
//    if (index == start_index)
//      map_cloud->points.insert(map_cloud->points.begin(),
//                               target_cloud->points.begin(), target_cloud->points.end());
//
//    sprintf(buf, "%03d", index + 1);
//    source_pcd_file = prefix + std::string(buf) + ".pcd";
//    source_pose_file = prefix + std::string(buf) + ".pose";
//    doublesource_odometry[3];
//    if (pcl::io::loadPCDFile<pcl::PointXYZ> (source_pcd_file, *source_cloud) == -1) //* load the file
//    {
//      PCL_ERROR ("Couldn't read file %s!\n", source_pcd_file.c_str());
//      return (-1);
//    }
//    PCL_INFO ("Loaded %d points: (width %d, height: %d) from %s.\n",
//              source_cloud->points.size(), source_cloud->width, source_cloud->height, source_pcd_file.c_str());
//    std::ifstream source_pose_in(source_pose_file.c_str());
//    source_pose_in >> source_odometry[0] >> source_odometry[1] >> tmp >> tmp >> tmp >> source_odometry[2];
//    PCL_INFO ("source odometry (x, y, theta): (%f, %f, %f)!\n", source_odometry[0], source_odometry[1], source_odometry[2]);
//
//    Eigen::Matrix4d guess(Eigen::Matrix4d::Zero());
//    doubletheta = (source_odometry[2] - target_odometry[2]) * 3.1415926 / 180;
//    guess(0,0) = cos(theta);
//    guess(0,1) = -sin(theta);
//    guess(1,0) = sin(theta);
//    guess(1,1) = cos(theta);
//    guess(2,2) = 1;
//    guess(3,3) = 1;
//    guess(0,3) = source_odometry[0] - target_odometry[0];
//    guess(1,3) = source_odometry[1] - target_odometry[1];
//
//    pcl::transformPointCloud(*source_cloud, *source_cloud_registered, guess);
//    pcl::visualization::PCLVisualizer viewer ("visualization for translation");
//    viewer.addPointCloud (target_cloud, "target_cloud", 0);
//    viewer.addPointCloud (source_cloud_registered, "source_cloud_registered", 0);
//    viewer.addCoordinateSystem (1.0);
//    viewer.setBackgroundColor (1,1,1);
//    viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "target_cloud");
//    viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "source_cloud_registered");
//    viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_COLOR, 1.0, 0.0, 0.0, "target_cloud");
//    viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_COLOR, 0.0, 1.0, 0.0, "source_cloud_registered");
//    viewer.spin ();
//  }

  for (int index = start_index; index <= end_index; index++)
  {
    char buf[4];
    double tmp;
    sprintf(buf, "%03d", index);
    target_pcd_file = prefix + std::string(buf) + ".pcd";
    target_pose_file = prefix + std::string(buf) + ".pose";
    double target_odometry[3];
    if (pcl::io::loadPCDFile<pcl::PointXYZ> (target_pcd_file, *target_cloud) == -1) //* load the file
    {
      PCL_ERROR ("Couldn't read file %s!\n", target_pcd_file.c_str());
      return (-1);
    }
    PCL_INFO ("Loaded %d points: (width %d, height: %d) from %s.\n",
              target_cloud->points.size(), target_cloud->width, target_cloud->height, target_pcd_file.c_str());
    std::ifstream target_pose_in (target_pose_file.c_str());
    target_pose_in >> target_odometry[0] >> target_odometry[1] >> tmp >> tmp >> tmp >> target_odometry[2];
    PCL_INFO ("target odometry (x, y, theta): (%f, %f, %f)!\n", target_odometry[0], target_odometry[1], target_odometry[2]);
    Eigen::Matrix4f transformation(Eigen::Matrix4f::Zero());
    double theta = target_odometry[2] * 3.14 / 180;
    transformation(0,0) = cos(theta);
    transformation(0,1) = -sin(theta);
    transformation(1,0) = sin(theta);
    transformation(1,1) = cos(theta);
    transformation(2,2) = 1;
    transformation(3,3) = 1;
    transformation(0,3) = target_odometry[0];
    transformation(1,3) = target_odometry[1];
    pcl::transformPointCloud(*target_cloud, *target_cloud, transformation);
    map_cloud->points.insert(map_cloud->points.end(),
                             target_cloud->points.begin(),
                             target_cloud->points.end());
  }

  map_cloud->width = map_cloud->points.size();
  map_cloud->height = 1;
  pcl::io::savePCDFileBinary("map.pcd", *map_cloud);
  return 0;
}
int
main (int argc, char** argv)
{
  // Loading first scan of room.
  pcl::PointCloud<pcl::PointXYZ>::Ptr target_cloud (new pcl::PointCloud<pcl::PointXYZ>);
  if (pcl::io::loadPCDFile<pcl::PointXYZ> ("room_scan1.pcd", *target_cloud) == -1)
  {
    PCL_ERROR ("Couldn't read file room_scan1.pcd \n");
    return (-1);
  }
  std::cout << "Loaded " << target_cloud->size () << " data points from room_scan1.pcd" << std::endl;

  // Loading second scan of room from new perspective.
  pcl::PointCloud<pcl::PointXYZ>::Ptr input_cloud (new pcl::PointCloud<pcl::PointXYZ>);
  if (pcl::io::loadPCDFile<pcl::PointXYZ> ("room_scan2.pcd", *input_cloud) == -1)
  {
    PCL_ERROR ("Couldn't read file room_scan2.pcd \n");
    return (-1);
  }
  std::cout << "Loaded " << input_cloud->size () << " data points from room_scan2.pcd" << std::endl;

  // Filtering input scan to roughly 10% of original size to increase speed of registration.
  pcl::PointCloud<pcl::PointXYZ>::Ptr filtered_cloud (new pcl::PointCloud<pcl::PointXYZ>);
  pcl::ApproximateVoxelGrid<pcl::PointXYZ> approximate_voxel_filter;
  approximate_voxel_filter.setLeafSize (0.2, 0.2, 0.2);
  approximate_voxel_filter.setInputCloud (input_cloud);
  approximate_voxel_filter.filter (*filtered_cloud);
  std::cout << "Filtered cloud contains " << filtered_cloud->size ()
            << " data points from room_scan2.pcd" << std::endl;

  // Initializing Normal Distributions Transform (NDT).
  pcl::NormalDistributionsTransform<pcl::PointXYZ, pcl::PointXYZ> ndt;

  // Setting scale dependent NDT parameters
  // Setting minimum transformation difference for termination condition.
  ndt.setTransformationEpsilon (0.01);
  // Setting maximum step size for More-Thuente line search.
  ndt.setStepSize (0.1);
  //Setting Resolution of NDT grid structure (VoxelGridCovariance).
  ndt.setResolution (1.0);

  // Setting max number of registration iterations.
  ndt.setMaximumIterations (35);

  // Setting point cloud to be aligned.
  ndt.setInputCloud (filtered_cloud);
  // Setting point cloud to be aligned to.
  ndt.setInputTarget (target_cloud);

  // Set initial alignment estimate found using robot odometry.
  Eigen::AngleAxisf init_rotation (0.6931, Eigen::Vector3f::UnitZ ());
  Eigen::Translation3f init_translation (1.79387, 0.720047, 0);
  Eigen::Matrix4f init_guess = (init_translation * init_rotation).matrix ();

  // Calculating required rigid transform to align the input cloud to the target cloud.
  pcl::PointCloud<pcl::PointXYZ>::Ptr output_cloud (new pcl::PointCloud<pcl::PointXYZ>);
  ndt.align (*output_cloud, init_guess);

  std::cout << "Normal Distributions Transform has converged:" << ndt.hasConverged ()
            << " score: " << ndt.getFitnessScore () << std::endl;

  // Transforming unfiltered, input cloud using found transform.
  pcl::transformPointCloud (*input_cloud, *output_cloud, ndt.getFinalTransformation ());

  // Saving transformed input cloud.
  pcl::io::savePCDFileASCII ("room_scan2_transformed.pcd", *output_cloud);

  // Initializing point cloud visualizer
  boost::shared_ptr<pcl::visualization::PCLVisualizer>
  viewer_final (new pcl::visualization::PCLVisualizer ("3D Viewer"));
  viewer_final->setBackgroundColor (0, 0, 0);

  // Coloring and visualizing target cloud (red).
  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>
  target_color (target_cloud, 255, 0, 0);
  viewer_final->addPointCloud<pcl::PointXYZ> (target_cloud, target_color, "target cloud");
  viewer_final->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE,
                                                  1, "target cloud");

  // Coloring and visualizing transformed input cloud (green).
  pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>
  output_color (output_cloud, 0, 255, 0);
  viewer_final->addPointCloud<pcl::PointXYZ> (output_cloud, output_color, "output cloud");
  viewer_final->setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE,
                                                  1, "output cloud");

  // Starting visualizer
  viewer_final->addCoordinateSystem (1.0);
  viewer_final->initCameraParameters ();

  // Wait until visualizer window is closed.
  while (!viewer_final->wasStopped ())
  {
    viewer_final->spinOnce (100);
    boost::this_thread::sleep (boost::posix_time::microseconds (100000));
  }

  return (0);
}