Ejemplo n.º 1
1
/**
  * Code example for ICP taking 2 points clouds (2D or 3D) relatively close
  * and computing the transformation between them.
  *
  * This code is more complete than icp_simple. It can load parameter files and
  * has more options.
  */
int main(int argc, const char *argv[])
{
	bool isTransfoSaved = false;
	string configFile;
	string outputBaseFile("test");
	string format("vtk")
	string initTranslation("0,0,0");
	string initRotation("1,0,0;0,1,0;0,0,1");
	const int ret = validateArgs(argc, argv, isTransfoSaved, configFile,
								 outputBaseFile, format, initTranslation, initRotation);
	if (ret != 0)
	{
		return ret;
	}
	const char *refFile(argv[argc-2]);
	const char *dataFile(argv[argc-1]);

	// Load point clouds
	const DP ref(DP::load(refFile));
	const DP data(DP::load(dataFile));

	// Create the default ICP algorithm
	PM::ICP icp;

	if (configFile.empty())
	{
		// See the implementation of setDefault() to create a custom ICP algorithm
		icp.setDefault();
	}
	else
	{
		// load YAML config
		ifstream ifs(configFile.c_str());
		if (!ifs.good())
		{
			cerr << "Cannot open config file " << configFile << ", usage:"; usage(argv); exit(1);
		}
		icp.loadFromYaml(ifs);
	}

	int cloudDimension = ref.getEuclideanDim();
	
	if (!(cloudDimension == 2 || cloudDimension == 3)) 
	{
		cerr << "Invalid input point clouds dimension = " << cloudDimension << endl;
		exit(1);
	}

	PM::TransformationParameters translation =
			parseTranslation(initTranslation, cloudDimension);
	PM::TransformationParameters rotation =
			parseRotation(initRotation, cloudDimension);
	PM::TransformationParameters initTransfo = translation*rotation;

	PM::Transformation* rigidTrans;
	rigidTrans = PM::get().REG(Transformation).create("RigidTransformation");

	if (!rigidTrans->checkParameters(initTransfo)) {
		cerr << endl
			 << "Initial transformation is not rigid, identiy will be used"
			 << endl;
		initTransfo = PM::TransformationParameters::Identity(
					cloudDimension+1,cloudDimension+1);
	}

	const DP initializedData = rigidTrans->compute(data, initTransfo);

	// Compute the transformation to express data in ref
	PM::TransformationParameters T = icp(initializedData, ref);
	// cout << outputBaseFile << " match ratio: " << icp.errorMinimizer->getWeightedPointUsedRatio() << endl;

	// Transform data to express it in ref
	DP data_out(initializedData);
	icp.transformations.apply(data_out, T);

	// Safe files to see the results
	ref.save(outputBaseFile + "_ref." + format);
	data.save(outputBaseFile + "_data_in" + format);
	data_out.save(outputBaseFile + "_data_out" + format);
	if(isTransfoSaved) {
		ofstream transfoFile;
		string initFileName = outputBaseFile + "_init_transfo.txt";
		string icpFileName = outputBaseFile + "_icp_transfo.txt";
		string completeFileName = outputBaseFile + "_complete_transfo.txt";

		transfoFile.open(initFileName.c_str());
		if(transfoFile.is_open()) {
			transfoFile << initTransfo << endl;
			transfoFile.close();
		} else {
			cout << "Unable to write the initial transformation file\n" << endl;
		}

		transfoFile.open(icpFileName.c_str());
		if(transfoFile.is_open()) {
			transfoFile << T << endl;
			transfoFile.close();
		} else {
			cout << "Unable to write the ICP transformation file\n" << endl;
		}

		transfoFile.open(completeFileName.c_str());
		if(transfoFile.is_open()) {
			transfoFile << T*initTransfo << endl;
			transfoFile.close();
		} else {
			cout << "Unable to write the complete transformation file\n" << endl;
		}
	}
	else {
		cout << "ICP transformation:" << endl << T << endl;
	}

	return 0;
}
/**
  * Code example for ICP taking 2 points clouds (2D or 3D) relatively close
  * and computing the transformation between them.
  *
  * This code is more complete than icp_simple. It can load parameter files and
  * has more options.
  */
int main(int argc, const char *argv[])
{
	bool isTransfoSaved = false;
	string configFile;
	string outputBaseFile("test");
	string initTranslation("0,0,0");
	string initRotation("1,0,0;0,1,0;0,0,1");
	const int ret = validateArgs(argc, argv, isTransfoSaved, configFile,
								 outputBaseFile, initTranslation, initRotation);
	if (ret != 0)
	{
		return ret;
	}
	const char *refFile(argv[argc-2]);
	const char *dataFile(argv[argc-1]);

	// Load point clouds
	const DP ref(DP::load(refFile));
	const DP data(DP::load(dataFile));

	// Create the default ICP algorithm
	PM::ICP icp;

	if (configFile.empty())
	{
		// See the implementation of setDefault() to create a custom ICP algorithm
		icp.setDefault();
	}
	else
	{
		// load YAML config
		ifstream ifs(configFile.c_str());
		if (!ifs.good())
		{
			cerr << "Cannot open config file " << configFile << ", usage:"; usage(argv); exit(1);
		}
		icp.loadFromYaml(ifs);
	}

	int cloudDimension = ref.getEuclideanDim();
	
	if (!(cloudDimension == 2 || cloudDimension == 3)) 
	{
		cerr << "Invalid input point clouds dimension" << endl;
		exit(1);
	}

	PM::TransformationParameters translation =
			parseTranslation(initTranslation, cloudDimension);
	PM::TransformationParameters rotation =
			parseRotation(initRotation, cloudDimension);
	PM::TransformationParameters initTransfo = translation*rotation;

	PM::Transformation* rigidTrans;
	rigidTrans = PM::get().REG(Transformation).create("RigidTransformation");

	if (!rigidTrans->checkParameters(initTransfo)) {
		cerr << endl
			 << "Initial transformation is not rigid, identiy will be used"
			 << endl;
		initTransfo = PM::TransformationParameters::Identity(
					cloudDimension+1,cloudDimension+1);
	}

	const DP initializedData = rigidTrans->compute(data, initTransfo);

	// Compute the transformation to express data in ref
	PM::TransformationParameters T = icp(initializedData, ref);
	float matchRatio = icp.errorMinimizer->getWeightedPointUsedRatio();
	cout << "match ratio: " <<  matchRatio << endl;

	

	// Transform data to express it in ref
	DP data_out(initializedData);
	icp.transformations.apply(data_out, T);

	cout << endl << "------------------" << endl;
	
	// START demo 1
	// Test for retrieving Haussdorff distance (with outliers). We generate new matching module 
	// specifically for this purpose. 
	//
	// INPUTS:
	// ref: point cloud used as reference
	// data_out: aligned point cloud (using the transformation outputted by icp)
	// icp: icp object used to aligned the point clouds

	
	// Structure to hold future match results
	PM::Matches matches;

	Parametrizable::Parameters params;
	params["knn"] =  toParam(1); // for Hausdorff distance, we only need the first closest point
	params["epsilon"] =  toParam(0);

	PM::Matcher* matcherHausdorff = PM::get().MatcherRegistrar.create("KDTreeMatcher", params);
	
	// max. distance from reading to reference
	matcherHausdorff->init(ref);
	matches = matcherHausdorff->findClosests(data_out);
	float maxDist1 = matches.getDistsQuantile(1.0);
	float maxDistRobust1 = matches.getDistsQuantile(0.85);

	// max. distance from reference to reading
	matcherHausdorff->init(data_out);
	matches = matcherHausdorff->findClosests(ref);
	float maxDist2 = matches.getDistsQuantile(1.0);
	float maxDistRobust2 = matches.getDistsQuantile(0.85);

	float haussdorffDist = std::max(maxDist1, maxDist2);
	float haussdorffQuantileDist = std::max(maxDistRobust1, maxDistRobust2);

	cout << "Haussdorff distance: " << std::sqrt(haussdorffDist) << " m" << endl;
	cout << "Haussdorff quantile distance: " << std::sqrt(haussdorffQuantileDist) <<  " m" << endl;

	// START demo 2
	// Test for retrieving paired point mean distance without outliers. We reuse the same module used for 
	// the icp object.
	//
	// INPUTS:
	// ref: point cloud used as reference
	// data_out: aligned point cloud (using the transformation outputted by icp)
	// icp: icp object used to aligned the point clouds
	
	// initiate the matching with unfiltered point cloud
	icp.matcher->init(ref);

	// extract closest points
	matches = icp.matcher->findClosests(data_out);

	// weight paired points
	const PM::OutlierWeights outlierWeights = icp.outlierFilters.compute(data_out, ref, matches);
	
	// generate tuples of matched points and remove pairs with zero weight
	const PM::ErrorMinimizer::ErrorElements matchedPoints( data_out, ref, outlierWeights, matches);

	// extract relevant information for convenience
	const int dim = matchedPoints.reading.getEuclideanDim();
	const int nbMatchedPoints = matchedPoints.reading.getNbPoints(); 
	const PM::Matrix matchedRead = matchedPoints.reading.features.topRows(dim);
	const PM::Matrix matchedRef = matchedPoints.reference.features.topRows(dim);
	
	// compute mean distance
	const PM::Matrix dist = (matchedRead - matchedRef).colwise().norm(); // replace that by squaredNorm() to save computation time
	const float meanDist = dist.sum()/nbMatchedPoints;
	cout << "Robust mean distance: " << meanDist << " m" << endl;

	// END demo

	cout << "------------------" << endl << endl;


	// Safe files to see the results
	ref.save(outputBaseFile + "_ref.vtk");
	data.save(outputBaseFile + "_data_in.vtk");
	data_out.save(outputBaseFile + "_data_out.vtk");
	if(isTransfoSaved) {
		ofstream transfoFile;
		string initFileName = outputBaseFile + "_init_transfo.txt";
		string icpFileName = outputBaseFile + "_icp_transfo.txt";
		string completeFileName = outputBaseFile + "_complete_transfo.txt";

		transfoFile.open(initFileName.c_str());
		if(transfoFile.is_open()) {
			transfoFile << initTransfo << endl;
			transfoFile.close();
		} else {
			cout << "Unable to write the initial transformation file\n" << endl;
		}

		transfoFile.open(icpFileName.c_str());
		if(transfoFile.is_open()) {
			transfoFile << T << endl;
			transfoFile.close();
		} else {
			cout << "Unable to write the ICP transformation file\n" << endl;
		}

		transfoFile.open(completeFileName.c_str());
		if(transfoFile.is_open()) {
			transfoFile << T*initTransfo << endl;
			transfoFile.close();
		} else {
			cout << "Unable to write the complete transformation file\n" << endl;
		}
	}
	else {
		cout << "ICP transformation:" << endl << T << endl;
	}

	return 0;
}