kopia lustrzana https://github.com/OpenDroneMap/ODM
1738 wiersze
60 KiB
C++
Executable File
1738 wiersze
60 KiB
C++
Executable File
// to format log_ output; version 2018-02-18
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
using namespace std;
|
|
// PCL
|
|
#include <pcl/io/obj_io.h>
|
|
#include <pcl/common/transforms.h>
|
|
|
|
// OpenCV
|
|
#include <opencv2/imgproc/imgproc.hpp>
|
|
#include <opencv2/highgui/highgui.hpp>
|
|
|
|
// This
|
|
#include "Georef.hpp"
|
|
|
|
std::ostream& operator<<(std::ostream &os, const GeorefSystem &geo)
|
|
{
|
|
return os << geo.system_ << "\n" << static_cast<int>(geo.eastingOffset_) << " " << static_cast<int>(geo.northingOffset_);
|
|
}
|
|
|
|
GeorefGCP::GeorefGCP()
|
|
:x_(0.0), y_(0.0), z_(0.0), use_(false), localX_(0.0), localY_(0.0), localZ_(0.0),cameraIndex_(0), pixelX_(0.0), pixelY_(0.0), image_(""), idgcp_("")
|
|
{
|
|
}
|
|
|
|
GeorefGCP::~GeorefGCP()
|
|
{
|
|
}
|
|
|
|
void GeorefGCP::extractGCP(std::istringstream &gcpStream)
|
|
{
|
|
gcpStream >> x_ >> y_ >> z_ >> pixelX_ >> pixelY_ >> image_ >> idgcp_;
|
|
}
|
|
|
|
Vec3 GeorefGCP::getPos()
|
|
{
|
|
return Vec3(localX_,localY_,localZ_);
|
|
}
|
|
|
|
Vec3 GeorefGCP::getReferencedPos()
|
|
{
|
|
return Vec3(x_,y_,z_);
|
|
}
|
|
|
|
GeorefCamera::GeorefCamera()
|
|
:focalLength_(0.0), k1_(0.0), k2_(0.0), transform_(NULL), position_(NULL), pose_(NULL)
|
|
{
|
|
}
|
|
|
|
GeorefCamera::GeorefCamera(const GeorefCamera &other)
|
|
: focalLength_(other.focalLength_), k1_(other.k1_), k2_(other.k2_),
|
|
easting_(other.easting_), northing_(other.northing_), altitude_(other.altitude_),
|
|
transform_(NULL), position_(NULL), pose_(NULL)
|
|
{
|
|
if(NULL != other.transform_)
|
|
{
|
|
transform_ = new Eigen::Affine3f(*other.transform_);
|
|
}
|
|
if(NULL != other.position_)
|
|
{
|
|
position_ = new Eigen::Vector3f(*other.position_);
|
|
}
|
|
if(pose_ != other.pose_)
|
|
{
|
|
pose_ = new Eigen::Affine3f(*other.pose_);
|
|
}
|
|
}
|
|
|
|
GeorefCamera::~GeorefCamera()
|
|
{
|
|
if(NULL != transform_)
|
|
{
|
|
delete transform_;
|
|
transform_ = NULL;
|
|
}
|
|
if(NULL != position_)
|
|
{
|
|
delete position_;
|
|
position_ = NULL;
|
|
}
|
|
if(pose_ != NULL)
|
|
{
|
|
delete pose_;
|
|
pose_ = NULL;
|
|
}
|
|
}
|
|
|
|
void GeorefCamera::extractCamera(std::ifstream &bundleStream)
|
|
{
|
|
// Extract intrinsic parameters.
|
|
bundleStream >> focalLength_ >> k1_ >> k2_;
|
|
|
|
Eigen::Vector3f t;
|
|
Eigen::Matrix3f rot;
|
|
Eigen::Affine3f transform;
|
|
Eigen::Affine3f pose;
|
|
|
|
bundleStream >> transform(0,0); // Read rotation (0,0) from bundle file
|
|
bundleStream >> transform(0,1); // Read rotation (0,1) from bundle file
|
|
bundleStream >> transform(0,2); // Read rotation (0,2) from bundle file
|
|
|
|
bundleStream >> transform(1,0); // Read rotation (1,0) from bundle file
|
|
bundleStream >> transform(1,1); // Read rotation (1,1) from bundle file
|
|
bundleStream >> transform(1,2); // Read rotation (1,2) from bundle file
|
|
|
|
bundleStream >> transform(2,0); // Read rotation (2,0) from bundle file
|
|
bundleStream >> transform(2,1); // Read rotation (2,1) from bundle file
|
|
bundleStream >> transform(2,2); // Read rotation (2,2) from bundle file
|
|
|
|
bundleStream >> t(0); // Read translation (0,3) from bundle file
|
|
bundleStream >> t(1); // Read translation (1,3) from bundle file
|
|
bundleStream >> t(2); // Read translation (2,3) from bundle file
|
|
|
|
//
|
|
pose(0,0) = transform(0,0);
|
|
pose(0,1) = transform(0,1);
|
|
pose(0,2) = transform(0,2);
|
|
|
|
pose(1,0) = transform(1,0);
|
|
pose(1,1) = transform(1,1);
|
|
pose(1,2) = transform(1,2);
|
|
|
|
pose(2,0) = transform(2,0);
|
|
pose(2,1) = transform(2,1);
|
|
pose(2,2) = transform(2,2);
|
|
|
|
pose(0,3) = t(0);
|
|
pose(1,3) = t(1);
|
|
pose(2,3) = t(2);
|
|
|
|
pose(3,0) = 0.0;
|
|
pose(3,1) = 0.0;
|
|
pose(3,2) = 0.0;
|
|
pose(3,3) = 1.0;
|
|
|
|
pose = pose.inverse();
|
|
|
|
// Column negation
|
|
pose(0,2) = -1.0*pose(0,2);
|
|
pose(1,2) = -1.0*pose(1,2);
|
|
pose(2,2) = -1.0*pose(2,2);
|
|
|
|
pose(0,1) = -1.0*pose(0,1);
|
|
pose(1,1) = -1.0*pose(1,1);
|
|
pose(2,1) = -1.0*pose(2,1);
|
|
|
|
if (pose_ != NULL)
|
|
{
|
|
delete pose_;
|
|
pose_ = NULL;
|
|
}
|
|
|
|
pose_ = new Eigen::Affine3f(pose);
|
|
|
|
rot = transform.matrix().topLeftCorner<3,3>();
|
|
|
|
// Calculate translation according to -R't and store in vector.
|
|
t = -rot.transpose()*t;
|
|
|
|
transform(0,3) = t(0);
|
|
transform(1,3) = t(1);
|
|
transform(2,3) = t(2);
|
|
|
|
|
|
// Set transform and position.
|
|
if(NULL != transform_)
|
|
{
|
|
delete transform_;
|
|
transform_ = NULL;
|
|
}
|
|
|
|
transform_ = new Eigen::Affine3f(transform);
|
|
|
|
if(NULL != position_)
|
|
{
|
|
delete position_;
|
|
position_ = NULL;
|
|
}
|
|
position_ = new Eigen::Vector3f(t);
|
|
}
|
|
|
|
void GeorefCamera::extractCameraGeoref(std::istringstream &coordStream)
|
|
{
|
|
coordStream >> easting_ >> northing_ >> altitude_;
|
|
}
|
|
|
|
Vec3 GeorefCamera::getPos()
|
|
{
|
|
return Vec3((*position_)(0),(*position_)(1),(*position_)(2));
|
|
}
|
|
|
|
Vec3 GeorefCamera::getReferencedPos()
|
|
{
|
|
return Vec3(easting_,northing_,altitude_);
|
|
}
|
|
|
|
bool GeorefCamera::isValid()
|
|
{
|
|
return focalLength_ != 0 && k1_ != 0 && k2_ != 0;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream &os, const GeorefCamera &cam)
|
|
{
|
|
os << "Focal, k1, k2 : " << cam.focalLength_ << ", " << cam.k1_ << ", " << cam.k2_ << "\n";
|
|
if(NULL != cam.transform_)
|
|
{
|
|
os << "Transform :\n" << cam.transform_->matrix() << "\n";
|
|
}
|
|
else
|
|
{
|
|
os << "Transform :\nNULL\n";
|
|
}
|
|
if(NULL != cam.position_)
|
|
{
|
|
os << "Position :\n" << cam.position_->matrix() << "\n";
|
|
}
|
|
else
|
|
{
|
|
os << "Position :\nNULL\n";
|
|
}
|
|
os << "east, north, alt : " << cam.easting_ << ", " << cam.northing_ << ", " << cam.altitude_ << '\n';
|
|
return os;
|
|
}
|
|
|
|
Georef::Georef() : log_(false)
|
|
{
|
|
georeferencePointCloud_ = false;
|
|
useGCP_ = false;
|
|
bundleFilename_ = "";
|
|
inputCoordFilename_ = "";
|
|
outputCoordFilename_ = "";
|
|
inputObjFilename_ = "";
|
|
outputObjFilename_ = "";
|
|
exportCoordinateFile_ = false;
|
|
exportGeorefSystem_ = false;
|
|
}
|
|
|
|
Georef::~Georef()
|
|
{
|
|
}
|
|
|
|
int Georef::run(int argc, char *argv[])
|
|
{
|
|
try
|
|
{
|
|
parseArguments(argc, argv);
|
|
createGeoreferencedModel();
|
|
}
|
|
catch (const GeorefException& e)
|
|
{
|
|
log_.setIsPrintingInCout(true);
|
|
log_ << e.what() << "\n";
|
|
log_.print(logFile_);
|
|
return EXIT_FAILURE;
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
log_.setIsPrintingInCout(true);
|
|
log_ << "Error in Georef:\n";
|
|
log_ << e.what() << "\n";
|
|
log_.print(logFile_);
|
|
return EXIT_FAILURE;
|
|
}
|
|
catch (...)
|
|
{
|
|
log_.setIsPrintingInCout(true);
|
|
log_ << "Unknown error, terminating:\n";
|
|
log_.print(logFile_);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
log_.print(logFile_);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
void Georef::parseArguments(int argc, char *argv[])
|
|
{
|
|
bool outputSpecified = false;
|
|
bool outputPointCloudSpecified = false;
|
|
bool imageListSpecified = false;
|
|
bool gcpFileSpecified = false;
|
|
bool imageLocation = false;
|
|
// bool bundleResized = false;
|
|
bool outputCoordSpecified = false;
|
|
bool inputCoordSpecified = false;
|
|
|
|
logFile_ = std::string(argv[0]) + "_log.txt";
|
|
log_ << logFile_ << "\n";
|
|
|
|
finalTransformFile_ = std::string(argv[0]) + "_transform.txt";
|
|
|
|
// If no arguments were passed, print help.
|
|
if (argc == 1)
|
|
{
|
|
printHelp();
|
|
}
|
|
|
|
log_ << "Arguments given\n";
|
|
for(int argIndex = 1; argIndex < argc; ++argIndex)
|
|
{
|
|
log_ << argv[argIndex] << '\n';
|
|
}
|
|
|
|
log_ << '\n';
|
|
for(int argIndex = 1; argIndex < argc; ++argIndex)
|
|
{
|
|
// The argument to be parsed.
|
|
std::string argument = std::string(argv[argIndex]);
|
|
|
|
if(argument == "-help")
|
|
{
|
|
printHelp();
|
|
}
|
|
else if(argument == "-verbose")
|
|
{
|
|
log_.setIsPrintingInCout(true);
|
|
}
|
|
else if (argument == "-logFile")
|
|
{
|
|
++argIndex;
|
|
if (argIndex >= argc)
|
|
{
|
|
throw GeorefException("Missing argument for '" + argument + "'.");
|
|
}
|
|
logFile_ = std::string(argv[argIndex]);
|
|
std::ofstream testFile(logFile_.c_str());
|
|
if (!testFile.is_open())
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' has a bad value.");
|
|
}
|
|
log_ << "Log file path was set to: " << logFile_ << "\n";
|
|
}
|
|
else if (argument == "-outputTransformFile")
|
|
{
|
|
++argIndex;
|
|
if (argIndex >= argc)
|
|
{
|
|
throw GeorefException("Missing argument for '" + argument + "'.");
|
|
}
|
|
finalTransformFile_ = std::string(argv[argIndex]);
|
|
std::ofstream testFile(logFile_.c_str());
|
|
if (!testFile.is_open())
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' has a bad value.");
|
|
}
|
|
log_ << "Transform file path was set to: " << finalTransformFile_ << "\n";
|
|
}
|
|
else if(argument == "-bundleFile" && argIndex < argc)
|
|
{
|
|
argIndex++;
|
|
if (argIndex >= argc)
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
|
|
}
|
|
bundleFilename_ = std::string(argv[argIndex]);
|
|
log_ << "Reading cameras from: " << bundleFilename_ << "\n";
|
|
}
|
|
else if(argument == "-inputCoordFile" && argIndex < argc)
|
|
{
|
|
argIndex++;
|
|
if (argIndex >= argc)
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
|
|
}
|
|
inputCoordFilename_ = std::string(argv[argIndex]);
|
|
log_ << "Reading cameras gps exif positions from: " << inputCoordFilename_ << "\n";
|
|
inputCoordSpecified = true;
|
|
}
|
|
else if(argument == "-outputCoordFile" && argIndex < argc)
|
|
{
|
|
argIndex++;
|
|
if (argIndex >= argc)
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
|
|
}
|
|
outputCoordFilename_ = std::string(argv[argIndex]);
|
|
log_ << "Exporting cameras georeferenced gps positions to: " << outputCoordFilename_ << "\n";
|
|
exportCoordinateFile_ = true;
|
|
outputCoordSpecified = true;
|
|
}
|
|
else if(argument == "-inputFile" && argIndex < argc)
|
|
{
|
|
argIndex++;
|
|
if (argIndex >= argc)
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
|
|
}
|
|
inputObjFilename_ = std::string(argv[argIndex]);
|
|
log_ << "Reading textured mesh from: " << inputObjFilename_ << "\n";
|
|
}
|
|
else if(argument == "-inputPointCloudFile" && argIndex < argc)
|
|
{
|
|
argIndex++;
|
|
if (argIndex >= argc)
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
|
|
}
|
|
inputPointCloudFilename_ = std::string(argv[argIndex]);
|
|
log_ << "Reading point cloud from: " << inputPointCloudFilename_ << "\n";
|
|
georeferencePointCloud_ = true;
|
|
}
|
|
else if(argument == "-gcpFile" && argIndex < argc)
|
|
{
|
|
argIndex++;
|
|
if (argIndex >= argc)
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
|
|
}
|
|
gcpFilename_ = std::string(argv[argIndex]);
|
|
log_ << "Reading GCPs from: " << gcpFilename_ << "\n";
|
|
gcpFileSpecified = true;
|
|
}
|
|
else if(argument == "-imagesListPath" && argIndex < argc)
|
|
{
|
|
argIndex++;
|
|
if (argIndex >= argc)
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
|
|
}
|
|
imagesListPath_ = std::string(argv[argIndex]);
|
|
log_ << "Reading image list from: " << imagesListPath_ << "\n";
|
|
imageListSpecified = true;
|
|
}
|
|
else if(argument == "-imagesPath" && argIndex < argc)
|
|
{
|
|
argIndex++;
|
|
if (argIndex >= argc)
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
|
|
}
|
|
imagesLocation_ = std::string(argv[argIndex]);
|
|
log_ << "Images location is set to: " << imagesLocation_ << "\n";
|
|
imageLocation = true;
|
|
}
|
|
else if(argument == "-georefFileOutputPath" && argIndex < argc)
|
|
{
|
|
argIndex++;
|
|
if (argIndex >= argc)
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
|
|
}
|
|
georefFilename_ = std::string(argv[argIndex]);
|
|
log_ << "Georef file output path is set to: " << georefFilename_ << "\n";
|
|
exportGeorefSystem_ = true;
|
|
}
|
|
/*else if(argument == "-bundleResizedTo" && argIndex < argc)
|
|
{
|
|
argIndex++;
|
|
if (argIndex >= argc)
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
|
|
}
|
|
std::stringstream ss(argv[argIndex]);
|
|
ss >> bundleResizedTo_;
|
|
if (ss.bad())
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' has a bad value. (wrong type)");
|
|
}
|
|
log_ << "Bundle resize value is set to: " << bundleResizedTo_ << "\n";
|
|
bundleResized = true;
|
|
}*/
|
|
else if(argument == "-outputFile" && argIndex < argc)
|
|
{
|
|
argIndex++;
|
|
if (argIndex >= argc)
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
|
|
}
|
|
outputObjFilename_ = std::string(argv[argIndex]);
|
|
log_ << "Writing output to: " << outputObjFilename_ << "\n";
|
|
outputSpecified = true;
|
|
}
|
|
else if(argument == "-outputPointCloudFile" && argIndex < argc)
|
|
{
|
|
argIndex++;
|
|
if (argIndex >= argc)
|
|
{
|
|
throw GeorefException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
|
|
}
|
|
outputPointCloudFilename_ = std::string(argv[argIndex]);
|
|
log_ << "Writing output to: " << outputPointCloudFilename_ << "\n";
|
|
outputPointCloudSpecified = true;
|
|
}
|
|
else
|
|
{
|
|
printHelp();
|
|
throw GeorefException("Unrecognised argument '" + argument + "'");
|
|
}
|
|
}
|
|
|
|
if (inputCoordSpecified && outputCoordSpecified)
|
|
{
|
|
throw GeorefException("Both output and input coordfile specified, only one of those are accepted.");
|
|
}
|
|
|
|
if (imageListSpecified && gcpFileSpecified && imageLocation ) // && bundleResized)
|
|
{
|
|
useGCP_ = true;
|
|
}
|
|
else
|
|
{
|
|
log_ << '\n';
|
|
log_ << "Missing input in order to use GCP for georeferencing. Using EXIF data instead.\n";
|
|
}
|
|
|
|
if(georeferencePointCloud_ && !outputPointCloudSpecified)
|
|
{
|
|
setDefaultPointCloudOutput();
|
|
}
|
|
|
|
if(!outputSpecified)
|
|
{
|
|
setDefaultOutput();
|
|
}
|
|
}
|
|
|
|
void Georef::printHelp()
|
|
{
|
|
bool printInCoutPop = log_.isPrintingInCout();
|
|
log_.setIsPrintingInCout(true);
|
|
|
|
log_ << "Georef.exe\n\n";
|
|
|
|
log_ << "Purpose:" << "\n";
|
|
log_ << "Georeference a textured mesh with the use of ground control points or exif data from the images." << "\n";
|
|
|
|
log_ << "Usage:" << "\n";
|
|
log_ << "The program requires a path to a camera bundle file, a camera georeference coords file, and an input OBJ mesh file. All other input parameters are optional." << "\n\n";
|
|
|
|
log_ << "The following flags are available\n";
|
|
log_ << "Call the program with flag \"-help\", or without parameters to print this message, or check any generated log file.\n";
|
|
log_ << "Call the program with flag \"-verbose\", to print log messages in the standard output stream as well as in the log file.\n\n";
|
|
|
|
log_ << "Parameters are specified as: \"-<argument name> <argument>\", (without <>), and the following parameters are configureable: " << "\n";
|
|
log_ << "\"-bundleFile <path>\" (mandatory)" << "\n";
|
|
log_ << "\"Input cameras bundle file.\n\n";
|
|
|
|
log_ << "\"-gcpFile <path>\" (mandatory if using ground control points)\n";
|
|
log_ << "Path to the file containing the ground control points used for georeferencing.\n";
|
|
log_ << "The file needs to be on the following line format:\n";
|
|
log_ << "easting northing height pixelrow pixelcol imagename\n\n";
|
|
|
|
log_ << "\"-inputCoordFile <path>\" (mandatory if using exif data)" << "\n";
|
|
log_ << "\"Input cameras geroreferenced coords file.\n\n";
|
|
|
|
log_ << "\"-outputCoordFile <path>\" (optional)" << "\n";
|
|
log_ << "\"Output cameras geroreferenced coords file.\n\n";
|
|
|
|
log_ << "\"-inputFile <path>\" (mandatory)" << "\n";
|
|
log_ << "\"Input obj file that must contain a textured mesh.\n\n";
|
|
|
|
log_ << "\"-inputPointCloudFile <path>\" (optional)" << "\n";
|
|
log_ << "\"Input ply file that must contain a point cloud.\n\n";
|
|
|
|
log_ << "\"-imagesListPath <path>\" (mandatory if using ground control points)\n";
|
|
log_ << "Path to the list containing the image names used in the bundle.out file.\n\n";
|
|
|
|
log_ << "\"-imagesPath <path>\" (mandatory if using ground control points)\n";
|
|
log_ << "Path to the folder containing full resolution images.\n\n";
|
|
|
|
// log_ << "\"-bundleResizedTo <integer>\" (mandatory if using ground control points)\n";
|
|
// log_ << "The resized resolution used in bundler.\n\n";
|
|
|
|
log_ << "\"-outputFile <path>\" (optional, default <inputFile>_geo)" << "\n";
|
|
log_ << "\"Output obj file that will contain the georeferenced texture mesh.\n\n";
|
|
|
|
log_ << "\"-outputPointCloudFile <path>\" (mandatory if georeferencing a point cloud)" << "\n";
|
|
log_ << "\"Output ply file that will contain the georeferenced point cloud.\n\n";
|
|
|
|
log_.setIsPrintingInCout(printInCoutPop);
|
|
}
|
|
|
|
void Georef::setDefaultOutput()
|
|
{
|
|
if(inputObjFilename_.empty())
|
|
{
|
|
throw GeorefException("Tried to generate default output file without having an input file.");
|
|
}
|
|
|
|
std::string tmp = inputObjFilename_;
|
|
size_t findPos = tmp.find_last_of(".");
|
|
|
|
if(std::string::npos == findPos)
|
|
{
|
|
throw GeorefException("Tried to generate default ouptut file, could not find .obj in the input file:\n\'"+inputObjFilename_+"\'");
|
|
}
|
|
|
|
tmp = tmp.substr(0, findPos);
|
|
|
|
outputObjFilename_ = tmp + "_geo.obj";
|
|
log_ << "Writing output to: " << outputObjFilename_ << "\n";
|
|
}
|
|
|
|
void Georef::setDefaultPointCloudOutput()
|
|
{
|
|
if(inputPointCloudFilename_.empty())
|
|
{
|
|
throw GeorefException("Tried to generate default point cloud ouptut file without having an input file.");
|
|
}
|
|
|
|
std::string tmp = inputPointCloudFilename_;
|
|
size_t findPos = tmp.find_last_of(".");
|
|
|
|
if(std::string::npos == findPos)
|
|
{
|
|
throw GeorefException("Tried to generate default ouptut file, could not find .ply in the input file:\n\'"+inputPointCloudFilename_+"\'");
|
|
}
|
|
|
|
tmp = tmp.substr(0, findPos);
|
|
|
|
outputPointCloudFilename_ = tmp + "_geo.ply";
|
|
log_ << "Writing output to: " << outputPointCloudFilename_ << "\n";
|
|
}
|
|
|
|
void Georef::createGeoreferencedModel()
|
|
{
|
|
if (useGCP_)
|
|
{
|
|
createGeoreferencedModelFromGCPData();
|
|
}
|
|
else
|
|
{
|
|
createGeoreferencedModelFromExifData();
|
|
}
|
|
}
|
|
|
|
void Georef::readCameras()
|
|
{
|
|
// Read translations from bundle file
|
|
std::ifstream bundleStream(bundleFilename_.c_str());
|
|
if (!bundleStream.good())
|
|
{
|
|
throw GeorefException("Failed opening bundle file " + bundleFilename_ + " for reading." + '\n');
|
|
}
|
|
|
|
// Read Cameras.
|
|
std::string bundleLine;
|
|
std::getline(bundleStream, bundleLine); // Read past bundle version comment
|
|
int numCameras, numPoints;
|
|
bundleStream >> numCameras >> numPoints;
|
|
for (int i=0; i<numCameras; ++i)
|
|
{
|
|
cameras_.push_back(GeorefCamera());
|
|
cameras_.back().extractCamera(bundleStream);
|
|
}
|
|
}
|
|
|
|
void Georef::readGCPs()
|
|
{
|
|
std::ifstream imageListStream(imagesListPath_.c_str());
|
|
if (!imageListStream.good())
|
|
{
|
|
throw GeorefException("Failed opening image path " + imagesListPath_ + " for reading.\n");
|
|
}
|
|
|
|
for (size_t i=0; i<cameras_.size(); ++i)
|
|
{
|
|
std::string imageName;
|
|
imageListStream >> imageName;
|
|
imageList_.push_back(imageName);
|
|
}
|
|
|
|
// Number of GCPs read
|
|
size_t nrGCPs = 0;
|
|
|
|
std::ifstream gcpStream(gcpFilename_.c_str());
|
|
if (!gcpStream.good())
|
|
{
|
|
throw GeorefException("Failed opening gcp file " + gcpFilename_ + " for reading.\n");
|
|
}
|
|
std::string gcpString;
|
|
|
|
// Read the first line in the file as the format of the projected coordinates
|
|
std::getline(gcpStream, georefSystem_.system_);
|
|
|
|
log_ << '\n';
|
|
log_<< "Reading following GCPs from file:\n";
|
|
|
|
// Read all GCPs
|
|
while(std::getline(gcpStream, gcpString))
|
|
{
|
|
std::istringstream istr(gcpString);
|
|
GeorefGCP gcp;
|
|
gcp.extractGCP(istr);
|
|
gcps_.push_back(gcp);
|
|
++nrGCPs;
|
|
|
|
// log_<<"x_: "<<gcp.x_<<" y_: "<<gcp.y_<<" z_: "<<gcp.z_<<" pixelX_: "<<gcp.pixelX_<<" pixelY_: "<<gcp.pixelY_<<" image: "<<gcp.image_<<"\n";
|
|
log_<< setiosflags(ios::fixed) << setprecision(3) <<"x_: "<< gcp.x_ <<" y_: "<< gcp.y_<<" z_: "<< gcp.z_ <<" pixelX_: "<<gcp.pixelX_<<" pixelY_: "<<gcp.pixelY_<<" image: "<<gcp.image_ <<" idgcp_: " << gcp.idgcp_ << "\n"; // more readeable
|
|
}
|
|
|
|
// Check if the GCPs have corresponding images in the bundle files and if they don't, remove them from the GCP-list
|
|
for (size_t gcpIndex = 0; gcpIndex<gcps_.size(); ++gcpIndex)
|
|
{
|
|
bool imageExists = false;
|
|
for (size_t cameraIndex = 0; cameraIndex < cameras_.size(); ++cameraIndex)
|
|
{
|
|
size_t found = imageList_[cameraIndex].find(gcps_[gcpIndex].image_);
|
|
if (found != std::string::npos)
|
|
{
|
|
gcps_[gcpIndex].cameraIndex_ = cameraIndex;
|
|
imageExists = true;
|
|
}
|
|
}
|
|
if (!imageExists)
|
|
{
|
|
log_ <<"Can't find image "<<gcps_[gcpIndex].image_<<". The corresponding GCP will not be used for georeferencing.\n";
|
|
gcps_.erase(gcps_.begin() + gcpIndex);
|
|
--gcpIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Georef::calculateGCPOffset()
|
|
{
|
|
// Offsets
|
|
double eastingOffset = 0;
|
|
double northingOffset = 0;
|
|
|
|
// Add all GCPs to weight an offset
|
|
for (size_t gcpIndex = 0; gcpIndex<gcps_.size(); ++gcpIndex)
|
|
{
|
|
eastingOffset += (gcps_[gcpIndex].x_)/static_cast<double>(gcps_.size());
|
|
northingOffset += (gcps_[gcpIndex].y_)/static_cast<double>(gcps_.size());
|
|
}
|
|
|
|
georefSystem_.eastingOffset_ = static_cast<int>(std::floor(eastingOffset));
|
|
georefSystem_.northingOffset_ = static_cast<int>(std::floor(northingOffset));
|
|
|
|
log_ << '\n';
|
|
log_<<"The calculated easting offset for the georeferenced system: "<<georefSystem_.eastingOffset_<<"\n";
|
|
log_<<"The calculated northing offset for the georeferenced system: "<<georefSystem_.northingOffset_<<"\n";
|
|
|
|
log_ << '\n';
|
|
log_ << "Recalculated GCPs with offset:\n";
|
|
|
|
// Subtract the offset from all GCPs
|
|
for (size_t gcpIndex = 0; gcpIndex<gcps_.size(); ++gcpIndex)
|
|
{
|
|
gcps_[gcpIndex].x_ -= static_cast<double>(georefSystem_.eastingOffset_);
|
|
gcps_[gcpIndex].y_ -= static_cast<double>(georefSystem_.northingOffset_);
|
|
log_<<"x_: "<<gcps_[gcpIndex].x_<<" y_: "<<gcps_[gcpIndex].y_<<" z_: "<<gcps_[gcpIndex].z_<<"\n";
|
|
}
|
|
}
|
|
|
|
pcl::PointXYZ Georef::barycentricCoordinates(pcl::PointXY point, pcl::PointXYZ vert0, pcl::PointXYZ vert1, pcl::PointXYZ vert2, pcl::PointXY p0, pcl::PointXY p1, pcl::PointXY p2)
|
|
{
|
|
// Shorthands
|
|
double x0 = p0.x; double y0 = p0.y;
|
|
double x1 = p1.x; double y1 = p1.y;
|
|
double x2 = p2.x; double y2 = p2.y;
|
|
double x = point.x; double y = point.y;
|
|
|
|
double q1x = x1 - x0;
|
|
double q1y = y1 - y0;
|
|
double q2x = x2 - x0;
|
|
double q2y = y2 - y0;
|
|
|
|
double norm = q1x * q2y - q1y * q2x;
|
|
double l1 = q2y*(x - x0) - q2x*(y - y0);
|
|
l1 /= norm;
|
|
double l2 = -q1y*(x - x0) + q1x*(y - y0);
|
|
l2 /= norm;
|
|
|
|
pcl::PointXYZ res;
|
|
res.x = (1.0 - l1 - l2)*vert0.x + l1*vert1.x + l2*vert2.x;
|
|
res.y = (1.0 - l1 - l2)*vert0.y + l1*vert1.y + l2*vert2.y;
|
|
res.z = (1.0 - l1 - l2)*vert0.z + l1*vert1.z + l2*vert2.z;
|
|
|
|
return res;
|
|
}
|
|
|
|
void Georef::performGeoreferencingWithGCP()
|
|
{
|
|
log_ << '\n';
|
|
log_ << "Reading mesh file " << inputObjFilename_ <<"\n";
|
|
log_ << '\n';
|
|
pcl::TextureMesh mesh;
|
|
if (loadObjFile(inputObjFilename_, mesh) == -1)
|
|
{
|
|
throw GeorefException("Error when reading model from:\n" + inputObjFilename_ + "\n");
|
|
}
|
|
else
|
|
{
|
|
log_ << "Successfully loaded " << inputObjFilename_ << ".\n";
|
|
}
|
|
|
|
// Convert vertices to pcl::PointXYZ cloud
|
|
pcl::PointCloud<pcl::PointXYZ>::Ptr meshCloud (new pcl::PointCloud<pcl::PointXYZ>);
|
|
pcl::fromPCLPointCloud2 (mesh.cloud, *meshCloud);
|
|
|
|
// The number of GCP that is usable
|
|
int nrGCPUsable = 0;
|
|
|
|
for (size_t gcpIndex = 0; gcpIndex < gcps_.size(); ++gcpIndex)
|
|
{
|
|
// Bool to check if the GCP is intersecting any triangle
|
|
bool exists = false;
|
|
|
|
// Translate the GeoreferenceCamera to pcl-format in order to use pcl-functions
|
|
pcl::TextureMapping<pcl::PointXYZ>::Camera cam;
|
|
cam.focal_length = cameras_[gcps_[gcpIndex].cameraIndex_].focalLength_;
|
|
cam.pose = *(cameras_[gcps_[gcpIndex].cameraIndex_].pose_);
|
|
cam.texture_file = imagesLocation_ + '/' + gcps_[gcpIndex].image_;
|
|
|
|
cv::Mat image = cv::imread(cam.texture_file);
|
|
cam.height = static_cast<double>(image.rows);
|
|
cam.width = static_cast<double>(image.cols);
|
|
|
|
// The pixel position for the GCP in pcl-format in order to use pcl-functions
|
|
pcl::PointXY gcpPos;
|
|
gcpPos.x = static_cast<float>(gcps_[gcpIndex].pixelX_);
|
|
gcpPos.y = static_cast<float>(gcps_[gcpIndex].pixelY_);
|
|
|
|
// Move vertices in mesh into the camera coordinate system
|
|
pcl::PointCloud<pcl::PointXYZ>::Ptr cameraCloud (new pcl::PointCloud<pcl::PointXYZ>);
|
|
pcl::transformPointCloud (*meshCloud, *cameraCloud, cam.pose.inverse());
|
|
|
|
// The vertex indicies to be used in order to calculate the GCP in the models coordinates
|
|
size_t vert0Index = 0; size_t vert1Index = 0; size_t vert2Index = 0;
|
|
|
|
pcl::PointXY bestPixelPos0; pcl::PointXY bestPixelPos1; pcl::PointXY bestPixelPos2;
|
|
|
|
// The closest distance of a triangle to the camera
|
|
double bestDistance = std::numeric_limits<double>::infinity();
|
|
|
|
// Loop through all submeshes in model
|
|
for (size_t meshIndex = 0; meshIndex < mesh.tex_polygons.size(); ++meshIndex)
|
|
{
|
|
// Loop through all faces in submesh and check if inside polygon
|
|
for (size_t faceIndex = 0; faceIndex < mesh.tex_polygons[meshIndex].size(); ++faceIndex)
|
|
{
|
|
// Variables for the vertices in face as projections in the camera plane
|
|
pcl::PointXY pixelPos0; pcl::PointXY pixelPos1; pcl::PointXY pixelPos2;
|
|
if (isFaceProjected(cam,
|
|
cameraCloud->points[mesh.tex_polygons[meshIndex][faceIndex].vertices[0]],
|
|
cameraCloud->points[mesh.tex_polygons[meshIndex][faceIndex].vertices[1]],
|
|
cameraCloud->points[mesh.tex_polygons[meshIndex][faceIndex].vertices[2]],
|
|
pixelPos0, pixelPos1, pixelPos2))
|
|
{
|
|
// If the pixel position of the GCP is inside the current triangle
|
|
if (checkPointInsideTriangle(pixelPos0, pixelPos1, pixelPos2, gcpPos))
|
|
{
|
|
// Extract distances for all vertices for face to camera
|
|
double d0 = cameraCloud->points[mesh.tex_polygons[meshIndex][faceIndex].vertices[0]].z;
|
|
double d1 = cameraCloud->points[mesh.tex_polygons[meshIndex][faceIndex].vertices[1]].z;
|
|
double d2 = cameraCloud->points[mesh.tex_polygons[meshIndex][faceIndex].vertices[2]].z;
|
|
|
|
// Calculate largest distance and store in distance variable
|
|
double distance = std::max(d0, std::max(d1,d2));
|
|
|
|
// If the triangle is closer to the camera use this triangle
|
|
if (distance < bestDistance)
|
|
{
|
|
// Update variables for the closest polygon
|
|
bestDistance = distance;
|
|
vert0Index = mesh.tex_polygons[meshIndex][faceIndex].vertices[0];
|
|
vert1Index = mesh.tex_polygons[meshIndex][faceIndex].vertices[1];
|
|
vert2Index = mesh.tex_polygons[meshIndex][faceIndex].vertices[2];
|
|
bestPixelPos0 = pixelPos0;
|
|
bestPixelPos1 = pixelPos1;
|
|
bestPixelPos2 = pixelPos2;
|
|
exists = true;
|
|
++nrGCPUsable;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(exists)
|
|
{
|
|
// Shorthands for the vertices
|
|
pcl::PointXYZ v0 = meshCloud->points[vert0Index];
|
|
pcl::PointXYZ v1 = meshCloud->points[vert1Index];
|
|
pcl::PointXYZ v2 = meshCloud->points[vert2Index];
|
|
// Use barycentric coordinates to calculate position for the polygon intersection
|
|
pcl::PointXYZ gcpLocal = barycentricCoordinates(gcpPos, v0, v1, v2, bestPixelPos0, bestPixelPos1, bestPixelPos2);
|
|
|
|
log_ << "Position in model for gcp " << gcpIndex + 1<< ": x=" <<gcpLocal.x<<" y="<<gcpLocal.y<<" z="<<gcpLocal.z<<"\n";
|
|
gcps_[gcpIndex].localX_ = gcpLocal.x;
|
|
gcps_[gcpIndex].localY_ = gcpLocal.y;
|
|
gcps_[gcpIndex].localZ_ = gcpLocal.z;
|
|
gcps_[gcpIndex].use_ = true;
|
|
}
|
|
}
|
|
|
|
if (nrGCPUsable < 3)
|
|
{
|
|
throw GeorefException("Fewer than 3 GCPs have correspondences in the generated model.");
|
|
}
|
|
|
|
size_t gcp0; size_t gcp1; size_t gcp2;
|
|
log_ << '\n';
|
|
log_ << "Choosing optimal gcp triplet...\n";
|
|
chooseBestGCPTriplet(gcp0, gcp1, gcp2);
|
|
log_ << "Optimal gcp triplet chosen: ";
|
|
log_ << gcp0 << ", " << gcp1 << ", " << gcp2 << '\n';
|
|
log_ << '\n';
|
|
FindTransform transFinal;
|
|
transFinal.findTransform(gcps_[gcp0].getPos(), gcps_[gcp1].getPos(), gcps_[gcp2].getPos(),
|
|
gcps_[gcp0].getReferencedPos(), gcps_[gcp1].getReferencedPos(), gcps_[gcp2].getReferencedPos());
|
|
log_ << "Final transform:\n";
|
|
log_ << transFinal.transform_ << '\n';
|
|
|
|
printFinalTransform(transFinal.transform_);
|
|
|
|
// The transform used to transform model into the georeferenced system.
|
|
Eigen::Transform<float, 3, Eigen::Affine> transform;
|
|
|
|
transform(0, 0) = static_cast<float>(transFinal.transform_.r1c1_);
|
|
transform(1, 0) = static_cast<float>(transFinal.transform_.r2c1_);
|
|
transform(2, 0) = static_cast<float>(transFinal.transform_.r3c1_);
|
|
transform(3, 0) = static_cast<float>(transFinal.transform_.r4c1_);
|
|
|
|
transform(0, 1) = static_cast<float>(transFinal.transform_.r1c2_);
|
|
transform(1, 1) = static_cast<float>(transFinal.transform_.r2c2_);
|
|
transform(2, 1) = static_cast<float>(transFinal.transform_.r3c2_);
|
|
transform(3, 1) = static_cast<float>(transFinal.transform_.r4c2_);
|
|
|
|
transform(0, 2) = static_cast<float>(transFinal.transform_.r1c3_);
|
|
transform(1, 2) = static_cast<float>(transFinal.transform_.r2c3_);
|
|
transform(2, 2) = static_cast<float>(transFinal.transform_.r3c3_);
|
|
transform(3, 2) = static_cast<float>(transFinal.transform_.r4c3_);
|
|
|
|
transform(0, 3) = static_cast<float>(transFinal.transform_.r1c4_);
|
|
transform(1, 3) = static_cast<float>(transFinal.transform_.r2c4_);
|
|
transform(2, 3) = static_cast<float>(transFinal.transform_.r3c4_);
|
|
transform(3, 3) = static_cast<float>(transFinal.transform_.r4c4_);
|
|
|
|
log_ << '\n';
|
|
log_ << "Applying transform to mesh...\n";
|
|
// Move the mesh into position.
|
|
pcl::transformPointCloud(*meshCloud, *meshCloud, transform);
|
|
log_ << ".. mesh transformed.\n";
|
|
|
|
// Update the mesh.
|
|
pcl::toPCLPointCloud2 (*meshCloud, mesh.cloud);
|
|
|
|
// Iterate over each part of the mesh (one per material), to make texture file paths relative the .mtl file.
|
|
for(size_t t = 0; t < mesh.tex_materials.size(); ++t)
|
|
{
|
|
// The material of the current submesh.
|
|
pcl::TexMaterial& material = mesh.tex_materials[t];
|
|
|
|
size_t find = material.tex_file.find_last_of("/\\");
|
|
if(std::string::npos != find)
|
|
{
|
|
material.tex_file = material.tex_file.substr(find + 1);
|
|
}
|
|
}
|
|
|
|
log_ << '\n';
|
|
if (saveOBJFile(outputObjFilename_, mesh, 8) == -1)
|
|
{
|
|
throw GeorefException("Error when saving model:\n" + outputObjFilename_ + "\n");
|
|
}
|
|
else
|
|
{
|
|
log_ << "Successfully saved model.\n";
|
|
}
|
|
|
|
if(georeferencePointCloud_)
|
|
{
|
|
//pcl::PointCloud2<pcl::PointNormal>::Ptr pointCloud;
|
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr pointCloud(new pcl::PointCloud<pcl::PointXYZRGBNormal>());
|
|
if(pcl::io::loadPLYFile<pcl::PointXYZRGBNormal> (inputPointCloudFilename_.c_str(), *pointCloud.get()) == -1) {
|
|
throw GeorefException("Error when reading point cloud:\n" + inputPointCloudFilename_ + "\n");
|
|
}
|
|
else
|
|
{
|
|
log_ << "Successfully loaded " << pointCloud->size() << " points with corresponding normals from file.\n";
|
|
}
|
|
log_ << '\n';
|
|
log_ << "Applying transform to point cloud...\n";
|
|
pcl::transformPointCloud(*pointCloud, *pointCloud, transform);
|
|
log_ << ".. point cloud transformed.\n";
|
|
|
|
pcl::PLYWriter plyWriter;
|
|
|
|
log_ << '\n';
|
|
log_ << "Saving point cloud file to \'" << outputPointCloudFilename_ << "\'...\n";
|
|
//pcl::io::savePLYFileASCII(outputPointCloudFilename_.c_str(), *pointCloud.get());
|
|
plyWriter.write(outputPointCloudFilename_.c_str(), *pointCloud.get(), false, false);
|
|
log_ << ".. point cloud file saved.\n";
|
|
}
|
|
|
|
if(exportCoordinateFile_)
|
|
{
|
|
log_ << '\n';
|
|
log_ << "Saving georeferenced camera positions to ";
|
|
log_ << outputCoordFilename_;
|
|
log_<< "\n";
|
|
std::ofstream coordStream(outputCoordFilename_.c_str());
|
|
coordStream << georefSystem_.system_ <<std::endl;
|
|
coordStream << static_cast<int>(georefSystem_.eastingOffset_) << " " << static_cast<int>(georefSystem_.northingOffset_) << std::endl;
|
|
for(size_t cameraIndex = 0; cameraIndex < cameras_.size(); ++cameraIndex)
|
|
{
|
|
Vec3 globalCameraPosition = (transFinal.transform_)*(cameras_[cameraIndex].getPos());
|
|
coordStream << globalCameraPosition.x_ << " " << globalCameraPosition.y_ << " " << globalCameraPosition.z_ << std::endl;
|
|
}
|
|
coordStream.close();
|
|
log_ << "...coordinate file saved.\n";
|
|
}
|
|
|
|
if(exportGeorefSystem_)
|
|
{
|
|
printGeorefSystem();
|
|
}
|
|
}
|
|
|
|
void Georef::createGeoreferencedModelFromGCPData()
|
|
{
|
|
readCameras();
|
|
|
|
readGCPs();
|
|
|
|
calculateGCPOffset();
|
|
|
|
performGeoreferencingWithGCP();
|
|
|
|
}
|
|
|
|
void Georef::createGeoreferencedModelFromExifData()
|
|
{
|
|
readCameras();
|
|
|
|
// Read coords from coord file generated by extract_utm tool
|
|
std::ifstream coordStream(inputCoordFilename_.c_str());
|
|
if (!coordStream.good())
|
|
{
|
|
throw GeorefException("Failed opening coordinate file " + inputCoordFilename_ + " for reading." + '\n');
|
|
}
|
|
|
|
std::string coordString;
|
|
std::getline(coordStream, georefSystem_.system_); // System
|
|
{
|
|
std::getline(coordStream, coordString);
|
|
std::stringstream ss(coordString);
|
|
|
|
ss >> georefSystem_.eastingOffset_ >> georefSystem_.northingOffset_;
|
|
}
|
|
|
|
log_ << '\n';
|
|
log_ << "Geographical reference system\n";
|
|
log_ << georefSystem_ << '\n';
|
|
|
|
// The number of cameras in the coords file.
|
|
size_t nGeorefCameras = 0;
|
|
|
|
// Read the georefernced position for all cameras.
|
|
while (std::getline(coordStream, coordString))
|
|
{
|
|
if(nGeorefCameras >= cameras_.size())
|
|
{
|
|
throw GeorefException("Error, to many cameras in \'" + inputCoordFilename_ + "\' coord file.\n");
|
|
}
|
|
|
|
std::istringstream istr(coordString);
|
|
cameras_[nGeorefCameras].extractCameraGeoref(istr);
|
|
|
|
++nGeorefCameras;
|
|
}
|
|
coordStream.close();
|
|
|
|
if(nGeorefCameras < cameras_.size())
|
|
{
|
|
throw GeorefException("Not enough cameras in \'" + inputCoordFilename_ + "\' coord file.\n");
|
|
}
|
|
|
|
// Remove invalid cameras
|
|
std::vector<GeorefCamera> goodCameras;
|
|
for (size_t i = 0; i < cameras_.size(); i++){
|
|
if (cameras_[i].isValid()) goodCameras.push_back(GeorefCamera(cameras_[i]));
|
|
}
|
|
cameras_.clear();
|
|
cameras_ = goodCameras;
|
|
|
|
// The optimal camera triplet.
|
|
size_t cam0, cam1, cam2;
|
|
|
|
log_ << '\n';
|
|
log_ << "Choosing optimal camera triplet...\n";
|
|
chooseBestCameraTriplet(cam0, cam1, cam2);
|
|
log_ << "... optimal camera triplet chosen:\n";
|
|
log_ << cam0 << ", " << cam1 << ", " << cam2 << '\n';
|
|
log_ << '\n';
|
|
FindTransform transFinal;
|
|
transFinal.findTransform(cameras_[cam0].getPos(), cameras_[cam1].getPos(), cameras_[cam2].getPos(),
|
|
cameras_[cam0].getReferencedPos(), cameras_[cam1].getReferencedPos(), cameras_[cam2].getReferencedPos());
|
|
log_ << "Final transform:\n";
|
|
log_ << transFinal.transform_ << '\n';
|
|
|
|
printFinalTransform(transFinal.transform_);
|
|
|
|
// The transform used to move the chosen area into the ortho photo.
|
|
Eigen::Transform<float, 3, Eigen::Affine> transform;
|
|
|
|
transform(0, 0) = static_cast<float>(transFinal.transform_.r1c1_); transform(1, 0) = static_cast<float>(transFinal.transform_.r2c1_);
|
|
transform(2, 0) = static_cast<float>(transFinal.transform_.r3c1_); transform(3, 0) = static_cast<float>(transFinal.transform_.r4c1_);
|
|
|
|
transform(0, 1) = static_cast<float>(transFinal.transform_.r1c2_); transform(1, 1) = static_cast<float>(transFinal.transform_.r2c2_);
|
|
transform(2, 1) = static_cast<float>(transFinal.transform_.r3c2_); transform(3, 1) = static_cast<float>(transFinal.transform_.r4c2_);
|
|
|
|
transform(0, 2) = static_cast<float>(transFinal.transform_.r1c3_); transform(1, 2) = static_cast<float>(transFinal.transform_.r2c3_);
|
|
transform(2, 2) = static_cast<float>(transFinal.transform_.r3c3_); transform(3, 2) = static_cast<float>(transFinal.transform_.r4c3_);
|
|
|
|
transform(0, 3) = static_cast<float>(transFinal.transform_.r1c4_); transform(1, 3) = static_cast<float>(transFinal.transform_.r2c4_);
|
|
transform(2, 3) = static_cast<float>(transFinal.transform_.r3c4_); transform(3, 3) = static_cast<float>(transFinal.transform_.r4c4_);
|
|
|
|
log_ << '\n';
|
|
log_ << "Reading mesh file...\n";
|
|
pcl::TextureMesh mesh;
|
|
loadObjFile(inputObjFilename_, mesh);
|
|
log_ << ".. mesh file read.\n";
|
|
|
|
// Contains the vertices of the mesh.
|
|
pcl::PointCloud<pcl::PointXYZ>::Ptr meshCloud (new pcl::PointCloud<pcl::PointXYZ>);
|
|
pcl::fromPCLPointCloud2 (mesh.cloud, *meshCloud);
|
|
|
|
log_ << '\n';
|
|
log_ << "Applying transform to mesh...\n";
|
|
// Move the mesh into position.
|
|
pcl::transformPointCloud(*meshCloud, *meshCloud, transform);
|
|
log_ << ".. mesh transformed.\n";
|
|
|
|
// Update the mesh.
|
|
pcl::toPCLPointCloud2 (*meshCloud, mesh.cloud);
|
|
|
|
// Iterate over each part of the mesh (one per material), to make texture file paths relative the .mtl file.
|
|
for(size_t t = 0; t < mesh.tex_materials.size(); ++t)
|
|
{
|
|
// The material of the current submesh.
|
|
pcl::TexMaterial& material = mesh.tex_materials[t];
|
|
|
|
size_t find = material.tex_file.find_last_of("/\\");
|
|
if(std::string::npos != find)
|
|
{
|
|
material.tex_file = material.tex_file.substr(find + 1);
|
|
}
|
|
}
|
|
|
|
log_ << '\n';
|
|
log_ << "Saving mesh file to \'" << outputObjFilename_ << "\'...\n";
|
|
saveOBJFile(outputObjFilename_, mesh, 8);
|
|
log_ << ".. mesh file saved.\n";
|
|
|
|
if(georeferencePointCloud_)
|
|
{
|
|
//pcl::PointCloud2<pcl::PointNormal>::Ptr pointCloud;
|
|
pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr pointCloud(new pcl::PointCloud<pcl::PointXYZRGBNormal>());
|
|
if(pcl::io::loadPLYFile<pcl::PointXYZRGBNormal> (inputPointCloudFilename_.c_str(), *pointCloud.get()) == -1) {
|
|
throw GeorefException("Error when reading point cloud:\n" + inputPointCloudFilename_ + "\n");
|
|
}
|
|
else
|
|
{
|
|
log_ << "Successfully loaded " << pointCloud->size() << " points with corresponding normals from file.\n";
|
|
}
|
|
log_ << '\n';
|
|
log_ << "Applying transform to point cloud...\n";
|
|
pcl::transformPointCloud(*pointCloud, *pointCloud, transform);
|
|
log_ << ".. point cloud transformed.\n";
|
|
|
|
pcl::PLYWriter plyWriter;
|
|
|
|
log_ << '\n';
|
|
log_ << "Saving point cloud file to \'" << outputPointCloudFilename_ << "\'...\n";
|
|
//pcl::io::savePLYFileASCII(outputPointCloudFilename_.c_str(), *pointCloud.get());
|
|
plyWriter.write(outputPointCloudFilename_.c_str(), *pointCloud.get(), false, false);
|
|
log_ << ".. point cloud file saved.\n";
|
|
}
|
|
|
|
if(exportGeorefSystem_)
|
|
{
|
|
printGeorefSystem();
|
|
}
|
|
}
|
|
|
|
void Georef::chooseBestGCPTriplet(size_t &gcp0, size_t &gcp1, size_t &gcp2)
|
|
{
|
|
size_t numThreads = boost::thread::hardware_concurrency();
|
|
boost::thread_group threads;
|
|
std::vector<GeorefBestTriplet*> triplets;
|
|
for(size_t t = 0; t < numThreads; ++t)
|
|
{
|
|
GeorefBestTriplet* triplet = new GeorefBestTriplet();
|
|
triplets.push_back(triplet);
|
|
threads.create_thread(boost::bind(&Georef::findBestGCPTriplet, this, boost::ref(triplet->t_), boost::ref(triplet->s_), boost::ref(triplet->p_), t, numThreads, boost::ref(triplet->err_)));
|
|
}
|
|
|
|
threads.join_all();
|
|
|
|
double minTotError = std::numeric_limits<double>::infinity();
|
|
for(size_t t = 0; t<numThreads; t++)
|
|
{
|
|
GeorefBestTriplet* triplet = triplets[t];
|
|
if(minTotError > triplet->err_)
|
|
{
|
|
minTotError = triplet->err_;
|
|
gcp0 = triplet->t_;
|
|
gcp1 = triplet->s_;
|
|
gcp2 = triplet->p_;
|
|
}
|
|
delete triplet;
|
|
}
|
|
|
|
log_ << "Mean georeference error " << minTotError / static_cast<double>(gcps_.size()) << '\n';
|
|
}
|
|
|
|
void Georef::findBestGCPTriplet(size_t &gcp0, size_t &gcp1, size_t &gcp2, size_t offset, size_t stride, double &minTotError)
|
|
{
|
|
minTotError = std::numeric_limits<double>::infinity();
|
|
|
|
for(size_t t = offset; t < gcps_.size(); t+=stride)
|
|
{
|
|
if (gcps_[t].use_)
|
|
{
|
|
for(size_t s = t; s < gcps_.size(); ++s)
|
|
{
|
|
if (gcps_[s].use_)
|
|
{
|
|
for(size_t p = s; p < gcps_.size(); ++p)
|
|
{
|
|
if (gcps_[p].use_)
|
|
{
|
|
FindTransform trans;
|
|
trans.findTransform(gcps_[t].getPos(), gcps_[s].getPos(), gcps_[p].getPos(),
|
|
gcps_[t].getReferencedPos(), gcps_[s].getReferencedPos(), gcps_[p].getReferencedPos());
|
|
|
|
// The total error for the curren camera triplet.
|
|
double totError = 0.0;
|
|
|
|
for(size_t r = 0; r < gcps_.size(); ++r)
|
|
{
|
|
totError += trans.error(gcps_[r].getPos(), gcps_[r].getReferencedPos());
|
|
}
|
|
|
|
if(minTotError > totError)
|
|
{
|
|
minTotError = totError;
|
|
gcp0 = t;
|
|
gcp1 = s;
|
|
gcp2 = p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
log_ << '[' << offset+1 << " of " << stride << "] Mean georeference error " << minTotError / static_cast<double>(gcps_.size());
|
|
log_ << " (" << gcp0 << ", " << gcp1 << ", " << gcp2 << ")\n";
|
|
}
|
|
|
|
void Georef::chooseBestCameraTriplet(size_t &cam0, size_t &cam1, size_t &cam2)
|
|
{
|
|
size_t numThreads = boost::thread::hardware_concurrency();
|
|
boost::thread_group threads;
|
|
std::vector<GeorefBestTriplet*> triplets;
|
|
for(size_t t = 0; t < numThreads; ++t)
|
|
{
|
|
GeorefBestTriplet* triplet = new GeorefBestTriplet();
|
|
triplets.push_back(triplet);
|
|
threads.create_thread(boost::bind(&Georef::findBestCameraTriplet, this, boost::ref(triplet->t_), boost::ref(triplet->s_), boost::ref(triplet->p_), t, numThreads, boost::ref(triplet->err_)));
|
|
}
|
|
|
|
threads.join_all();
|
|
|
|
double minTotError = std::numeric_limits<double>::infinity();
|
|
for(size_t t = 0; t<numThreads; t++)
|
|
{
|
|
GeorefBestTriplet* triplet = triplets[t];
|
|
if(minTotError > triplet->err_)
|
|
{
|
|
minTotError = triplet->err_;
|
|
cam0 = triplet->t_;
|
|
cam1 = triplet->s_;
|
|
cam2 = triplet->p_;
|
|
}
|
|
delete triplet;
|
|
}
|
|
|
|
log_ << "Mean georeference error " << minTotError / static_cast<double>(cameras_.size()) << '\n';
|
|
}
|
|
|
|
void Georef::findBestCameraTriplet(size_t &cam0, size_t &cam1, size_t &cam2, size_t offset, size_t stride, double &minTotError)
|
|
{
|
|
minTotError = std::numeric_limits<double>::infinity();
|
|
|
|
for(size_t t = offset; t < cameras_.size(); t+=stride)
|
|
{
|
|
for(size_t s = t; s < cameras_.size(); ++s)
|
|
{
|
|
for(size_t p = s; p < cameras_.size(); ++p)
|
|
{
|
|
FindTransform trans;
|
|
trans.findTransform(cameras_[t].getPos(), cameras_[s].getPos(), cameras_[p].getPos(),
|
|
cameras_[t].getReferencedPos(), cameras_[s].getReferencedPos(), cameras_[p].getReferencedPos());
|
|
|
|
// The total error for the current camera triplet.
|
|
double totError = 0.0;
|
|
|
|
for(size_t r = 0; r < cameras_.size(); ++r)
|
|
{
|
|
totError += trans.error(cameras_[r].getPos(), cameras_[r].getReferencedPos());
|
|
}
|
|
|
|
if(minTotError > totError)
|
|
{
|
|
minTotError = totError;
|
|
cam0 = t;
|
|
cam1 = s;
|
|
cam2 = p;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
log_ << '[' << offset+1 << " of " << stride << "] Mean georeference error " << minTotError / static_cast<double>(cameras_.size());
|
|
log_ << " (" << cam0 << ", " << cam1 << ", " << cam2 << ")\n";
|
|
}
|
|
|
|
void Georef::printGeorefSystem()
|
|
{
|
|
if(outputObjFilename_.empty())
|
|
{
|
|
throw GeorefException("Output file path empty!.");
|
|
}
|
|
|
|
std::string tmp = outputObjFilename_;
|
|
size_t findPos = tmp.find_last_of(".");
|
|
|
|
if(std::string::npos == findPos)
|
|
{
|
|
throw GeorefException("Tried to generate default ouptut file, could not find .obj in the output file:\n\'"+outputObjFilename_+"\'");
|
|
}
|
|
|
|
//tmp = tmp.substr(0, findPos);
|
|
|
|
//tmp = tmp + "_georef_system.txt";
|
|
log_ << '\n';
|
|
log_ << "Saving georeference system file to \'" << georefFilename_ << "\'...\n";
|
|
std::ofstream geoStream(georefFilename_.c_str());
|
|
geoStream << georefSystem_ << std::endl;
|
|
geoStream.close();
|
|
log_ << "... georeference system saved.\n";
|
|
}
|
|
|
|
|
|
void Georef::printFinalTransform(Mat4 transform)
|
|
{
|
|
if(outputObjFilename_.empty())
|
|
{
|
|
throw GeorefException("Output file path empty!.");
|
|
}
|
|
|
|
std::string tmp = outputObjFilename_;
|
|
size_t findPos = tmp.find_last_of(".");
|
|
|
|
if(std::string::npos == findPos)
|
|
{
|
|
throw GeorefException("Tried to generate default ouptut file, could not find .obj in the output file:\n\'"+outputObjFilename_+"\'");
|
|
}
|
|
|
|
log_ << '\n';
|
|
log_ << "Saving final transform file to \'" << finalTransformFile_ << "\'...\n";
|
|
std::ofstream transformStream(finalTransformFile_.c_str());
|
|
transformStream << transform << std::endl;
|
|
transformStream.close();
|
|
log_ << "... final transform saved.\n";
|
|
}
|
|
|
|
|
|
bool Georef::loadObjFile(std::string inputFile, pcl::TextureMesh &mesh)
|
|
{
|
|
int data_type;
|
|
unsigned int data_idx;
|
|
int file_version;
|
|
int offset = 0;
|
|
Eigen::Vector4f origin;
|
|
Eigen::Quaternionf orientation;
|
|
|
|
if (!readHeader(inputFile, mesh.cloud, origin, orientation, file_version, data_type, data_idx, offset))
|
|
{
|
|
throw GeorefException("Problem reading header in modelfile!\n");
|
|
}
|
|
|
|
std::ifstream fs;
|
|
|
|
fs.open (inputFile.c_str (), std::ios::binary);
|
|
if (!fs.is_open () || fs.fail ())
|
|
{
|
|
//PCL_ERROR ("[pcl::OBJReader::readHeader] Could not open file '%s'! Error : %s\n", file_name.c_str (), strerror(errno));
|
|
fs.close ();
|
|
log_<<"Could not read mesh from file ";
|
|
log_ << inputFile.c_str();
|
|
log_ <<"\n";
|
|
|
|
throw GeorefException("Problem reading mesh from file!\n");
|
|
}
|
|
|
|
// Seek at the given offset
|
|
fs.seekg (data_idx, std::ios::beg);
|
|
|
|
// Get normal_x field indices
|
|
int normal_x_field = -1;
|
|
for (std::size_t i = 0; i < mesh.cloud.fields.size (); ++i)
|
|
{
|
|
if (mesh.cloud.fields[i].name == "normal_x")
|
|
{
|
|
normal_x_field = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::size_t v_idx = 0;
|
|
std::size_t vn_idx = 0;
|
|
std::size_t vt_idx = 0;
|
|
std::size_t f_idx = 0;
|
|
std::string line;
|
|
std::vector<std::string> st;
|
|
std::vector<Eigen::Vector2f, Eigen::aligned_allocator<Eigen::Vector2f> > coordinates;
|
|
std::vector<Eigen::Vector2f, Eigen::aligned_allocator<Eigen::Vector2f> > allTexCoords;
|
|
|
|
std::map<int, int> f2vt;
|
|
|
|
try
|
|
{
|
|
while (!fs.eof ())
|
|
{
|
|
getline (fs, line);
|
|
// Ignore empty lines
|
|
if (line == "")
|
|
continue;
|
|
|
|
// Tokenize the line
|
|
std::stringstream sstream (line);
|
|
sstream.imbue (std::locale::classic ());
|
|
line = sstream.str ();
|
|
boost::trim (line);
|
|
boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on);
|
|
|
|
// Ignore comments
|
|
if (st[0] == "#")
|
|
continue;
|
|
// Vertex
|
|
if (st[0] == "v")
|
|
{
|
|
try
|
|
{
|
|
for (int i = 1, f = 0; i < 4; ++i, ++f)
|
|
{
|
|
float value = boost::lexical_cast<float> (st[i]);
|
|
memcpy (&mesh.cloud.data[v_idx * mesh.cloud.point_step + mesh.cloud.fields[f].offset], &value, sizeof (float));
|
|
}
|
|
|
|
++v_idx;
|
|
}
|
|
catch (const boost::bad_lexical_cast &e)
|
|
{
|
|
log_<<"Unable to convert %s to vertex coordinates!\n";
|
|
throw GeorefException("Unable to convert %s to vertex coordinates!");
|
|
}
|
|
continue;
|
|
}
|
|
// Vertex normal
|
|
if (st[0] == "vn")
|
|
{
|
|
try
|
|
{
|
|
for (int i = 1, f = normal_x_field; i < 4; ++i, ++f)
|
|
{
|
|
float value = boost::lexical_cast<float> (st[i]);
|
|
memcpy (&mesh.cloud.data[vn_idx * mesh.cloud.point_step + mesh.cloud.fields[f].offset],
|
|
&value,
|
|
sizeof (float));
|
|
}
|
|
++vn_idx;
|
|
}
|
|
catch (const boost::bad_lexical_cast &e)
|
|
{
|
|
log_<<"Unable to convert %s to vertex normal!\n";
|
|
throw GeorefException("Unable to convert %s to vertex normal!");
|
|
}
|
|
continue;
|
|
}
|
|
// Texture coordinates
|
|
if (st[0] == "vt")
|
|
{
|
|
try
|
|
{
|
|
Eigen::Vector3f c (0, 0, 0);
|
|
for (std::size_t i = 1; i < st.size (); ++i)
|
|
c[i-1] = boost::lexical_cast<float> (st[i]);
|
|
|
|
if (c[2] == 0)
|
|
coordinates.push_back (Eigen::Vector2f (c[0], c[1]));
|
|
else
|
|
coordinates.push_back (Eigen::Vector2f (c[0]/c[2], c[1]/c[2]));
|
|
++vt_idx;
|
|
|
|
}
|
|
catch (const boost::bad_lexical_cast &e)
|
|
{
|
|
log_<<"Unable to convert %s to vertex texture coordinates!\n";
|
|
throw GeorefException("Unable to convert %s to vertex texture coordinates!");
|
|
}
|
|
continue;
|
|
}
|
|
// Material
|
|
if (st[0] == "usemtl")
|
|
{
|
|
mesh.tex_polygons.push_back (std::vector<pcl::Vertices> ());
|
|
mesh.tex_materials.push_back (pcl::TexMaterial ());
|
|
for (std::size_t i = 0; i < companions_.size (); ++i)
|
|
{
|
|
std::vector<pcl::TexMaterial>::const_iterator mat_it = companions_[i].getMaterial (st[1]);
|
|
if (mat_it != companions_[i].materials_.end ())
|
|
{
|
|
mesh.tex_materials.back () = *mat_it;
|
|
break;
|
|
}
|
|
}
|
|
// We didn't find the appropriate material so we create it here with name only.
|
|
if (mesh.tex_materials.back ().tex_name == "")
|
|
mesh.tex_materials.back ().tex_name = st[1];
|
|
mesh.tex_coordinates.push_back (coordinates);
|
|
coordinates.clear ();
|
|
continue;
|
|
}
|
|
// Face
|
|
if (st[0] == "f")
|
|
{
|
|
//We only care for vertices indices
|
|
pcl::Vertices face_v; face_v.vertices.resize (st.size () - 1);
|
|
for (std::size_t i = 1; i < st.size (); ++i)
|
|
{
|
|
int v;
|
|
sscanf (st[i].c_str (), "%d", &v);
|
|
v = (v < 0) ? v_idx + v : v - 1;
|
|
face_v.vertices[i-1] = v;
|
|
|
|
int v2, vt, vn;
|
|
sscanf (st[i].c_str (), "%d/%d/%d", &v2, &vt, &vn);
|
|
f2vt[3*(f_idx) + i-1] = vt-1;
|
|
}
|
|
mesh.tex_polygons.back ().push_back (face_v);
|
|
++f_idx;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
catch (const char *exception)
|
|
{
|
|
fs.close ();
|
|
log_<<"Unable to read file!\n";
|
|
throw GeorefException("Unable to read file!");
|
|
}
|
|
|
|
if (vt_idx != v_idx)
|
|
{
|
|
std::vector<Eigen::Vector2f, Eigen::aligned_allocator<Eigen::Vector2f> > texcoordinates = std::vector<Eigen::Vector2f, Eigen::aligned_allocator<Eigen::Vector2f> >(0);
|
|
texcoordinates.reserve(3*f_idx);
|
|
|
|
for (size_t faceIndex = 0; faceIndex < f_idx; ++faceIndex)
|
|
{
|
|
for(size_t i = 0; i < 3; ++i)
|
|
{
|
|
Eigen::Vector2f vt = mesh.tex_coordinates[0][f2vt[3*faceIndex+i]];
|
|
texcoordinates.push_back(vt);
|
|
}
|
|
}
|
|
|
|
mesh.tex_coordinates.clear();
|
|
mesh.tex_coordinates.push_back(texcoordinates);
|
|
}
|
|
|
|
fs.close();
|
|
return (0);
|
|
}
|
|
|
|
bool Georef::readHeader (const std::string &file_name, pcl::PCLPointCloud2 &cloud,
|
|
Eigen::Vector4f &origin, Eigen::Quaternionf &orientation,
|
|
int &file_version, int &data_type, unsigned int &data_idx,
|
|
const int offset)
|
|
{
|
|
origin = Eigen::Vector4f::Zero ();
|
|
orientation = Eigen::Quaternionf::Identity ();
|
|
file_version = 0;
|
|
cloud.width = cloud.height = cloud.point_step = cloud.row_step = 0;
|
|
cloud.data.clear ();
|
|
data_type = 0;
|
|
data_idx = offset;
|
|
|
|
std::ifstream fs;
|
|
std::string line;
|
|
|
|
if (file_name == "" || !boost::filesystem::exists (file_name))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Open file in binary mode to avoid problem of
|
|
// std::getline() corrupting the result of ifstream::tellg()
|
|
fs.open (file_name.c_str (), std::ios::binary);
|
|
if (!fs.is_open () || fs.fail ())
|
|
{
|
|
fs.close ();
|
|
return false;
|
|
}
|
|
|
|
// Seek at the given offset
|
|
fs.seekg (offset, std::ios::beg);
|
|
|
|
// Read the header and fill it in with wonderful values
|
|
bool vertex_normal_found = false;
|
|
bool vertex_texture_found = false;
|
|
// Material library, skip for now!
|
|
// bool material_found = false;
|
|
std::vector<std::string> material_files;
|
|
std::size_t nr_point = 0;
|
|
std::vector<std::string> st;
|
|
|
|
try
|
|
{
|
|
while (!fs.eof ())
|
|
{
|
|
getline (fs, line);
|
|
// Ignore empty lines
|
|
if (line == "")
|
|
continue;
|
|
|
|
// Tokenize the line
|
|
std::stringstream sstream (line);
|
|
sstream.imbue (std::locale::classic ());
|
|
line = sstream.str ();
|
|
boost::trim (line);
|
|
boost::split (st, line, boost::is_any_of ("\t\r "), boost::token_compress_on);
|
|
// Ignore comments
|
|
if (st.at (0) == "#")
|
|
continue;
|
|
|
|
// Vertex
|
|
if (st.at (0) == "v")
|
|
{
|
|
++nr_point;
|
|
continue;
|
|
}
|
|
|
|
// Vertex texture
|
|
if ((st.at (0) == "vt") && !vertex_texture_found)
|
|
{
|
|
vertex_texture_found = true;
|
|
continue;
|
|
}
|
|
|
|
// Vertex normal
|
|
if ((st.at (0) == "vn") && !vertex_normal_found)
|
|
{
|
|
vertex_normal_found = true;
|
|
continue;
|
|
}
|
|
|
|
// Material library, skip for now!
|
|
if (st.at (0) == "mtllib")
|
|
{
|
|
material_files.push_back (st.at (1));
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
catch (const char *exception)
|
|
{
|
|
fs.close ();
|
|
return false;
|
|
}
|
|
|
|
if (!nr_point)
|
|
{
|
|
fs.close ();
|
|
return false;
|
|
}
|
|
|
|
int field_offset = 0;
|
|
for (int i = 0; i < 3; ++i, field_offset += 4)
|
|
{
|
|
cloud.fields.push_back (pcl::PCLPointField ());
|
|
cloud.fields[i].offset = field_offset;
|
|
cloud.fields[i].datatype = pcl::PCLPointField::FLOAT32;
|
|
cloud.fields[i].count = 1;
|
|
}
|
|
|
|
cloud.fields[0].name = "x";
|
|
cloud.fields[1].name = "y";
|
|
cloud.fields[2].name = "z";
|
|
|
|
if (vertex_normal_found)
|
|
{
|
|
std::string normals_names[3] = { "normal_x", "normal_y", "normal_z" };
|
|
for (int i = 0; i < 3; ++i, field_offset += 4)
|
|
{
|
|
cloud.fields.push_back (pcl::PCLPointField ());
|
|
pcl::PCLPointField& last = cloud.fields.back ();
|
|
last.name = normals_names[i];
|
|
last.offset = field_offset;
|
|
last.datatype = pcl::PCLPointField::FLOAT32;
|
|
last.count = 1;
|
|
}
|
|
}
|
|
|
|
if (material_files.size () > 0)
|
|
{
|
|
for (std::size_t i = 0; i < material_files.size (); ++i)
|
|
{
|
|
pcl::MTLReader companion;
|
|
|
|
if (companion.read (file_name, material_files[i]))
|
|
{
|
|
log_<<"Problem reading material file.";
|
|
}
|
|
|
|
companions_.push_back (companion);
|
|
}
|
|
}
|
|
|
|
cloud.point_step = field_offset;
|
|
cloud.width = nr_point;
|
|
cloud.height = 1;
|
|
cloud.row_step = cloud.point_step * cloud.width;
|
|
cloud.is_dense = true;
|
|
cloud.data.resize (cloud.point_step * nr_point);
|
|
fs.close ();
|
|
return true;
|
|
}
|
|
|