kopia lustrzana https://github.com/OpenDroneMap/ODM
Add Python files and update installation script
rodzic
c6932c459d
commit
3bd80dcab5
|
@ -8,4 +8,6 @@ odm_texturing-build/
|
|||
*.user
|
||||
cmvs.tar.gz
|
||||
parallel.tar.bz2
|
||||
LAStools.zip
|
||||
pcl.tar.gz
|
||||
*.pyc
|
||||
|
|
25
install.sh
25
install.sh
|
@ -41,6 +41,7 @@ echo " - script started - `date`"
|
|||
GRACLUS_PATH="$TOOLS_SRC_PATH/graclus"
|
||||
|
||||
PCL_PATH="$TOOLS_SRC_PATH/pcl"
|
||||
LASTOOLS_PATH="$TOOLS_SRC_PATH/lastools"
|
||||
ODM_MESHING_PATH="$TOOLS_SRC_PATH/odm_meshing"
|
||||
ODM_TEXTURING_PATH="$TOOLS_SRC_PATH/odm_texturing"
|
||||
ODM_ORTHOPHOTO_PATH="$TOOLS_SRC_PATH/odm_orthophoto"
|
||||
|
@ -120,6 +121,8 @@ sudo apt-get install --assume-yes --install-recommends \
|
|||
libzip-dev \
|
||||
libswitch-perl libjson-perl \
|
||||
libcv-dev libcvaux-dev libopencv-dev \
|
||||
gdal-bin \
|
||||
exiv2 \
|
||||
> "$TOOLS_LOG_PATH/apt-get_install.log" 2>&1
|
||||
else
|
||||
sudo apt-get install --assume-yes --install-recommends \
|
||||
|
@ -133,6 +136,8 @@ sudo apt-get install --assume-yes --install-recommends \
|
|||
libzip-dev \
|
||||
libswitch-perl \
|
||||
libcv-dev libcvaux-dev libopencv-dev \
|
||||
gdal-bin \
|
||||
exiv2 \
|
||||
> "$TOOLS_LOG_PATH/apt-get_install.log" 2>&1
|
||||
fi
|
||||
|
||||
|
@ -167,6 +172,7 @@ vlfeat.tar.gz http://www.vlfeat.org/download/vlfeat-0.9.13-bin.tar.gz
|
|||
cmvs.tar.gz http://www.di.ens.fr/cmvs/cmvs-fix2.tar.gz
|
||||
graclus.tar.gz http://smathermather.github.io/BundlerTools/patched_files/src/graclus/graclus1.2.tar.gz
|
||||
pcl.tar.gz https://github.com/PointCloudLibrary/pcl/archive/pcl-1.7.2.tar.gz
|
||||
LAStools.zip http://lastools.org/download/LAStools.zip
|
||||
EOF
|
||||
|
||||
echo " < done - `date`"
|
||||
|
@ -195,6 +201,8 @@ mv -f parallel-20141022 "$PARALLEL_PATH"
|
|||
mv -f PoissonRecon "$PSR_PATH"
|
||||
mv -f cmvs "$CMVS_PATH"
|
||||
mv -f pcl-pcl-1.7.2 "$PCL_PATH"
|
||||
mv -f LAStools "$LASTOOLS_PATH"
|
||||
|
||||
|
||||
|
||||
echo " < done - `date`"
|
||||
|
@ -308,6 +316,23 @@ echo " > vlfeat"
|
|||
echo " < done - `date`"
|
||||
echo
|
||||
|
||||
echo " > LAStools"
|
||||
cd "$LASTOOLS_PATH"
|
||||
|
||||
echo " - installing LAStools"
|
||||
|
||||
make > "$TOOLS_LOG_PATH/lastools_1_build.log" 2>&1
|
||||
|
||||
if [ "$ARCH" = "i686" ]; then
|
||||
cp -f "$LASTOOLS_PATH/bin/txt2las" "$TOOLS_BIN_PATH/txt2las"
|
||||
fi
|
||||
|
||||
if [ "$ARCH" = "x86_64" ]; then
|
||||
cp -f "$LASTOOLS_PATH/bin/txt2las" "$TOOLS_BIN_PATH/txt2las"
|
||||
fi
|
||||
echo " < done - `date`"
|
||||
echo
|
||||
|
||||
|
||||
echo " > cmvs/pmvs"
|
||||
cd "$CMVS_PATH/program/main"
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
import os
|
||||
import math
|
||||
import subprocess
|
||||
|
||||
# Purpose:
|
||||
# Preselect camera pairs for meature matching based by using GPS exif data.
|
||||
|
||||
def preselect_pairs(utm_extractor_path, image_list, k_distance, use_knn_mode = True, verbose = False):
|
||||
|
||||
# Parameterss:
|
||||
# utm_extractor_path: Path to the odm utm_extraction program that parses exif data.
|
||||
# images_path: Path to the folder containing the images.
|
||||
# k_distance: Nearest neighbor count in case of use_knn_mode = True, otherwise
|
||||
# the minimum distance between cameras to be matched.
|
||||
# use_knn_mode: True if knn mode is used to preselect matches, False for distance thresholding.
|
||||
|
||||
# Temporary files
|
||||
image_list_file = 'image_list_tmp.txt'
|
||||
utm_coord_file = 'utm_coord_tmp.txt'
|
||||
|
||||
# Parse the list of files with successfullt detectd points.
|
||||
image_folder = ""
|
||||
filenames = []
|
||||
with open(image_list, 'r') as keypoint_file:
|
||||
first_line = keypoint_file.readline()
|
||||
image_folder = first_line[:first_line.rfind("/")]
|
||||
keypoint_file.seek(0)
|
||||
for line in keypoint_file:
|
||||
filename = line[line.rfind("/")+1:line.rfind(".")]
|
||||
filename += ".jpg"
|
||||
filenames.append(filename)
|
||||
|
||||
data = open(image_list_file, "w")
|
||||
for filename in filenames:
|
||||
data.write("%s\n" % filename)
|
||||
data.close()
|
||||
|
||||
|
||||
# Define call parameters for calling the odm_extract_utm module
|
||||
utm_cmd = ''
|
||||
if verbose:
|
||||
utm_cmd = [utm_extractor_path, '-verbose', '-imagesPath', image_folder, \
|
||||
'-imageListFile', image_list_file, '-outputCoordFile', utm_coord_file]
|
||||
else:
|
||||
utm_cmd = [utm_extractor_path, '-imagesPath', image_folder, '-imageListFile', \
|
||||
image_list_file, '-outputCoordFile', utm_coord_file]
|
||||
|
||||
# Perform UTM extraction
|
||||
utm_process = subprocess.Popen(utm_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
output, err = utm_process.communicate()
|
||||
|
||||
# Check return code
|
||||
if utm_process.returncode != 0:
|
||||
print "Error when performing utm_extraction, dumping log file:"
|
||||
print output
|
||||
os.remove(image_list_file)
|
||||
return []
|
||||
|
||||
# Parse odm_extract_utm output
|
||||
coords = []
|
||||
with open('utm_coord_tmp.txt', 'r') as coord_file:
|
||||
# Skip header lines
|
||||
next(coord_file)
|
||||
next(coord_file)
|
||||
for line in coord_file:
|
||||
first_space = line.find(' ')
|
||||
second_space = first_space + line[first_space+1:].find(' ')
|
||||
xCoord = float(line[0: first_space])
|
||||
yCoord = float(line[first_space+1: second_space+1])
|
||||
coords.append([xCoord, yCoord])
|
||||
|
||||
# Calculate distances between camera pairs
|
||||
distances = []
|
||||
for xo,yo in coords:
|
||||
distances.append([])
|
||||
for xi, yi in coords:
|
||||
current_image = len(distances)
|
||||
xDist = xo-xi
|
||||
yDist = yo-yi
|
||||
distance = math.sqrt(xDist*xDist + yDist*yDist)
|
||||
current_image = len(distances[-1])
|
||||
distances[-1].append([current_image, distance, False])
|
||||
distances[-1].sort(key=lambda tup: tup[1])
|
||||
|
||||
# Select matched pairs and make there are no doubles
|
||||
matched_pairs = []
|
||||
if use_knn_mode:
|
||||
# Make sure that image count > k
|
||||
if len(coords) <= k_distance:
|
||||
print "Warning, k >= image count, the result will be equivalent to exhaustive matching"
|
||||
k_distance = len(coords)-1
|
||||
for outer_index, sorted_list in enumerate(distances):
|
||||
# Skip first element as distance will always be zero
|
||||
for sorted_index in xrange(1, k_distance+1):
|
||||
image_index, distance, dummy = distances[outer_index][sorted_index]
|
||||
is_added = distances[outer_index][image_index][2]
|
||||
if not is_added:
|
||||
matched_pairs.append([outer_index, image_index])
|
||||
distances[outer_index][image_index][2] = True
|
||||
distances[image_index][outer_index][2] = True
|
||||
else: # Distance mode
|
||||
for outer_index, sorted_list in enumerate(distances):
|
||||
# Skip first element as distance will always be zero
|
||||
for image_index, distance, dummy in sorted_list:
|
||||
if outer_index == image_index:
|
||||
continue
|
||||
if distance > k_distance:
|
||||
break
|
||||
is_added = distances[outer_index][image_index][2]
|
||||
if not is_added:
|
||||
matched_pairs.append([outer_index, image_index])
|
||||
distances[outer_index][image_index][2] = True
|
||||
distances[image_index][outer_index][2] = True
|
||||
|
||||
# Remove temporary files
|
||||
os.remove(image_list_file)
|
||||
os.remove(utm_coord_file)
|
||||
|
||||
return matched_pairs
|
|
@ -5,5 +5,5 @@
|
|||
int main (int argc, char **argv)
|
||||
{
|
||||
UtmExtractor utmExtractor;
|
||||
utmExtractor.run(argc, argv);
|
||||
return utmExtractor.run(argc, argv);
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ class OnMat3
|
|||
public:
|
||||
OnMat3(Vec3 r1, Vec3 r2, Vec3 r3);
|
||||
OnMat3(const OnMat3 &o);
|
||||
|
||||
|
||||
Vec3 r1_; /**< The first row of the matrix. **/
|
||||
Vec3 r2_; /**< The second row of the matrix. **/
|
||||
Vec3 r3_; /**< The third row of the matrix. **/
|
||||
|
|
|
@ -218,11 +218,15 @@ std::ostream& operator<<(std::ostream &os, const GeorefCamera &cam)
|
|||
|
||||
Georef::Georef() : log_(false)
|
||||
{
|
||||
georeferencePointCloud_ = false;
|
||||
useGCP_ = false;
|
||||
bundleFilename_ = "";
|
||||
coordFilename_ = "";
|
||||
inputCoordFilename_ = "";
|
||||
outputCoordFilename_ = "";
|
||||
inputObjFilename_ = "";
|
||||
outputObjFilename_ = "";
|
||||
exportCoordinateFile_ = false;
|
||||
exportGeorefSystem_ = false;
|
||||
}
|
||||
|
||||
Georef::~Georef()
|
||||
|
@ -267,10 +271,13 @@ int Georef::run(int argc, char *argv[])
|
|||
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";
|
||||
|
@ -326,15 +333,28 @@ void Georef::parseArguments(int argc, char *argv[])
|
|||
bundleFilename_ = std::string(argv[argIndex]);
|
||||
log_ << "Reading cameras from: " << bundleFilename_ << "\n";
|
||||
}
|
||||
else if(argument == "-coordFile" && argIndex < argc)
|
||||
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.");
|
||||
}
|
||||
coordFilename_ = std::string(argv[argIndex]);
|
||||
log_ << "Reading cameras georeferenced positions from: " << coordFilename_ << "\n";
|
||||
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)
|
||||
{
|
||||
|
@ -346,6 +366,17 @@ void Georef::parseArguments(int argc, char *argv[])
|
|||
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++;
|
||||
|
@ -379,6 +410,17 @@ void Georef::parseArguments(int argc, char *argv[])
|
|||
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++;
|
||||
|
@ -406,6 +448,17 @@ void Georef::parseArguments(int argc, char *argv[])
|
|||
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();
|
||||
|
@ -413,6 +466,11 @@ void Georef::parseArguments(int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
if (inputCoordSpecified && outputCoordSpecified)
|
||||
{
|
||||
throw GeorefException("Both output and input coordfile specified, only one of those are accepted.");
|
||||
}
|
||||
|
||||
if (imageListSpecified && gcpFileSpecified && imageLocation && bundleResized)
|
||||
{
|
||||
useGCP_ = true;
|
||||
|
@ -423,6 +481,11 @@ void Georef::parseArguments(int argc, char *argv[])
|
|||
log_ << "Missing input in order to use GCP for georeferencing. Using EXIF data instead.\n";
|
||||
}
|
||||
|
||||
if(georeferencePointCloud_ && !outputPointCloudSpecified)
|
||||
{
|
||||
setDefaultPointCloudOutput();
|
||||
}
|
||||
|
||||
if(!outputSpecified)
|
||||
{
|
||||
setDefaultOutput();
|
||||
|
@ -455,12 +518,18 @@ void Georef::printHelp()
|
|||
log_ << "The file needs to be on the following line format:\n";
|
||||
log_ << "easting northing height pixelrow pixelcol imagename\n\n";
|
||||
|
||||
log_ << "\"-coordFile <path>\" (mandatory if using exif data)" << "\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";
|
||||
|
||||
|
@ -472,6 +541,9 @@ void Georef::printHelp()
|
|||
|
||||
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);
|
||||
}
|
||||
|
@ -480,7 +552,7 @@ void Georef::setDefaultOutput()
|
|||
{
|
||||
if(inputObjFilename_.empty())
|
||||
{
|
||||
throw GeorefException("Tried to generate default ouptut file without having an input file.");
|
||||
throw GeorefException("Tried to generate default output file without having an input file.");
|
||||
}
|
||||
|
||||
std::string tmp = inputObjFilename_;
|
||||
|
@ -497,6 +569,27 @@ void Georef::setDefaultOutput()
|
|||
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_)
|
||||
|
@ -535,7 +628,7 @@ void Georef::readGCPs()
|
|||
std::ifstream imageListStream(imagesListPath_.c_str());
|
||||
if (!imageListStream.good())
|
||||
{
|
||||
throw GeorefException("Failed opening " + imagesListPath_ + " for reading.\n");
|
||||
throw GeorefException("Failed opening image path " + imagesListPath_ + " for reading.\n");
|
||||
}
|
||||
|
||||
for (size_t i=0; i<cameras_.size(); ++i)
|
||||
|
@ -551,7 +644,7 @@ void Georef::readGCPs()
|
|||
std::ifstream gcpStream(gcpFilename_.c_str());
|
||||
if (!gcpStream.good())
|
||||
{
|
||||
throw GeorefException("Failed opening " + gcpFilename_ + " for reading.\n");
|
||||
throw GeorefException("Failed opening gcp file " + gcpFilename_ + " for reading.\n");
|
||||
}
|
||||
std::string gcpString;
|
||||
|
||||
|
@ -820,7 +913,6 @@ void Georef::performGeoreferencingWithGCP()
|
|||
// 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)
|
||||
{
|
||||
|
@ -844,7 +936,53 @@ void Georef::performGeoreferencingWithGCP()
|
|||
log_ << "Successfully saved model.\n";
|
||||
}
|
||||
|
||||
printGeorefSystem();
|
||||
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()
|
||||
|
@ -864,10 +1002,10 @@ void Georef::createGeoreferencedModelFromExifData()
|
|||
readCameras();
|
||||
|
||||
// Read coords from coord file generated by extract_utm tool
|
||||
std::ifstream coordStream(coordFilename_.c_str());
|
||||
std::ifstream coordStream(inputCoordFilename_.c_str());
|
||||
if (!coordStream.good())
|
||||
{
|
||||
throw GeorefException("Failed opening " + coordFilename_ + " for reading." + '\n');
|
||||
throw GeorefException("Failed opening coordinate file " + inputCoordFilename_ + " for reading." + '\n');
|
||||
}
|
||||
|
||||
std::string coordString;
|
||||
|
@ -891,7 +1029,7 @@ void Georef::createGeoreferencedModelFromExifData()
|
|||
{
|
||||
if(nGeorefCameras >= cameras_.size())
|
||||
{
|
||||
throw GeorefException("Error, to many cameras in \'" + coordFilename_ + "\' coord file.\n");
|
||||
throw GeorefException("Error, to many cameras in \'" + inputCoordFilename_ + "\' coord file.\n");
|
||||
}
|
||||
|
||||
std::istringstream istr(coordString);
|
||||
|
@ -903,7 +1041,7 @@ void Georef::createGeoreferencedModelFromExifData()
|
|||
|
||||
if(nGeorefCameras < cameras_.size())
|
||||
{
|
||||
throw GeorefException("Not enough cameras in \'" + coordFilename_ + "\' coord file.\n");
|
||||
throw GeorefException("Not enough cameras in \'" + inputCoordFilename_ + "\' coord file.\n");
|
||||
}
|
||||
|
||||
// The optimal camera triplet.
|
||||
|
@ -938,7 +1076,6 @@ void Georef::createGeoreferencedModelFromExifData()
|
|||
|
||||
log_ << '\n';
|
||||
log_ << "Reading mesh file...\n";
|
||||
// The textureds mesh.e
|
||||
pcl::TextureMesh mesh;
|
||||
pcl::io::loadOBJFile(inputObjFilename_, mesh);
|
||||
log_ << ".. mesh file read.\n";
|
||||
|
@ -956,7 +1093,6 @@ void Georef::createGeoreferencedModelFromExifData()
|
|||
// 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)
|
||||
{
|
||||
|
@ -975,7 +1111,35 @@ void Georef::createGeoreferencedModelFromExifData()
|
|||
saveOBJFile(outputObjFilename_, mesh, 8);
|
||||
log_ << ".. mesh file saved.\n";
|
||||
|
||||
printGeorefSystem();
|
||||
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)
|
||||
|
@ -1073,12 +1237,12 @@ void Georef::printGeorefSystem()
|
|||
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.substr(0, findPos);
|
||||
|
||||
tmp = tmp + "_georef_system.txt";
|
||||
//tmp = tmp + "_georef_system.txt";
|
||||
log_ << '\n';
|
||||
log_ << "Saving georeference system file to \'" << tmp << "\'...\n";
|
||||
std::ofstream geoStream(tmp.c_str());
|
||||
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";
|
||||
|
|
|
@ -144,6 +144,11 @@ private:
|
|||
*/
|
||||
void setDefaultOutput();
|
||||
|
||||
/*!
|
||||
* \brief setDefaultPointCloudOutput Setup the output file name given the input file name.
|
||||
*/
|
||||
void setDefaultPointCloudOutput();
|
||||
|
||||
/*!
|
||||
* \brief createGeoreferencedModel Makes the input file georeferenced and saves it to the output file.
|
||||
*/
|
||||
|
@ -199,25 +204,32 @@ private:
|
|||
**/
|
||||
void printGeorefSystem();
|
||||
|
||||
Logger log_; /**< Logging object. */
|
||||
std::string logFile_; /**< The path to the output log file. */
|
||||
Logger log_; /**< Logging object. */
|
||||
std::string logFile_; /**< The path to the output log file. */
|
||||
|
||||
std::string bundleFilename_; /**< The path to the cameras bundle file. **/
|
||||
std::string coordFilename_; /**< The path to the cameras georeference file. **/
|
||||
std::string gcpFilename_; /**< The path to the GCP file **/
|
||||
std::string imagesListPath_; /**< Path to the image list. **/
|
||||
std::string imagesLocation_; /**< The folder containing the images in the image list. **/
|
||||
std::string inputObjFilename_; /**< The path to the input mesh obj file. **/
|
||||
std::string outputObjFilename_; /**< The path to the output mesh obj file. **/
|
||||
std::string bundleFilename_; /**< The path to the cameras bundle file. **/
|
||||
std::string inputCoordFilename_; /**< The path to the cameras exif gps positions file. **/
|
||||
std::string outputCoordFilename_; /**< The path to the cameras georeferenced gps positions file. **/
|
||||
std::string gcpFilename_; /**< The path to the GCP file **/
|
||||
std::string imagesListPath_; /**< Path to the image list. **/
|
||||
std::string imagesLocation_; /**< The folder containing the images in the image list. **/
|
||||
std::string inputObjFilename_; /**< The path to the input mesh obj file. **/
|
||||
std::string outputObjFilename_; /**< The path to the output mesh obj file. **/
|
||||
std::string inputPointCloudFilename_; /**< The path to the input point cloud file. **/
|
||||
std::string outputPointCloudFilename_; /**< The path to the output point cloud file. **/
|
||||
std::string georefFilename_; /**< The path to the output offset file. **/
|
||||
|
||||
bool useGCP_; /**< Check if GCP-file is present and use this to georeference the model. **/
|
||||
double bundleResizedTo_; /**< The size used in the previous steps to calculate the camera focal_length. */
|
||||
bool georeferencePointCloud_;
|
||||
bool exportCoordinateFile_;
|
||||
bool exportGeorefSystem_;
|
||||
bool useGCP_; /**< Check if GCP-file is present and use this to georeference the model. **/
|
||||
double bundleResizedTo_; /**< The size used in the previous steps to calculate the camera focal_length. */
|
||||
|
||||
std::vector<GeorefCamera> cameras_; /**< A vector of all cameras. **/
|
||||
std::vector<GeorefGCP> gcps_; /**< A vector of all GCPs. **/
|
||||
std::vector<std::string> imageList_; /**< A vector containing the names of the corresponding cameras. **/
|
||||
std::vector<GeorefCamera> cameras_; /**< A vector of all cameras. **/
|
||||
std::vector<GeorefGCP> gcps_; /**< A vector of all GCPs. **/
|
||||
std::vector<std::string> imageList_; /**< A vector containing the names of the corresponding cameras. **/
|
||||
|
||||
GeorefSystem georefSystem_; /**< Contains the georeference system. **/
|
||||
GeorefSystem georefSystem_; /**< Contains the georeference system. **/
|
||||
};
|
||||
|
||||
/*!
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// C++
|
||||
#include <math.h>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
||||
// This
|
||||
#include "OdmOrthoPhoto.hpp"
|
||||
|
@ -44,6 +45,7 @@ OdmOrthoPhoto::OdmOrthoPhoto()
|
|||
inputGeoRefFile_ = "";
|
||||
outputFile_ = "ortho.jpg";
|
||||
logFile_ = "log.txt";
|
||||
outputCornerFile_ = "";
|
||||
|
||||
resolution_ = 0.0f;
|
||||
|
||||
|
@ -229,6 +231,16 @@ void OdmOrthoPhoto::parseArguments(int argc, char *argv[])
|
|||
outputFile_ = std::string(argv[argIndex]);
|
||||
log_ << "Writing output to: " << outputFile_ << "\n";
|
||||
}
|
||||
else if(argument == "-outputCornerFile")
|
||||
{
|
||||
argIndex++;
|
||||
if (argIndex >= argc)
|
||||
{
|
||||
throw OdmOrthoPhotoException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
|
||||
}
|
||||
outputCornerFile_ = std::string(argv[argIndex]);
|
||||
log_ << "Writing corners to: " << outputCornerFile_ << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
printHelp();
|
||||
|
@ -258,12 +270,15 @@ void OdmOrthoPhoto::printHelp()
|
|||
log_ << "\"-inputFile <path>\" (mandatory)\n";
|
||||
log_ << "\"Input obj file that must contain a textured mesh.\n\n";
|
||||
|
||||
log_ << "\"-inputGeoRefFile <path>\" (optional, if specified boundary points are assumed to be givan as world coordinates. If not specified, the boundary points are assumed to be local coordinates)\n";
|
||||
log_ << "\"-inputGeoRefFile <path>\" (optional, if specified boundary points are assumed to be given as world coordinates. If not specified, the boundary points are assumed to be local coordinates)\n";
|
||||
log_ << "\"Input geograpical reference system file that describes the world position of the model's origin.\n\n";
|
||||
|
||||
log_ << "\"-outputFile <path>\" (optional, default: ortho.jpg)\n";
|
||||
log_ << "\"Target file in which the orthophoto is saved.\n\n";
|
||||
|
||||
log_ << "\"-outputCornerFile <path>\" (optional)\n";
|
||||
log_ << "\"Target text file for boundary corner points, written as \"xmin ymin xmax ymax\".\n\n";
|
||||
|
||||
log_ << "\"-resolution <pixels/m>\" (mandatory)\n";
|
||||
log_ << "\"The number of pixels used per meter.\n\n";
|
||||
|
||||
|
@ -364,7 +379,7 @@ void OdmOrthoPhoto::createOrthoPhoto()
|
|||
}
|
||||
|
||||
// Init ortho photo
|
||||
photo_ = cv::Mat::zeros(rowRes, colRes, CV_8UC3) + cv::Scalar(255, 255, 255);
|
||||
photo_ = cv::Mat::zeros(rowRes, colRes, CV_8UC4) + cv::Scalar(255, 255, 255, 0);
|
||||
depth_ = cv::Mat::zeros(rowRes, colRes, CV_32F) - std::numeric_limits<float>::infinity();
|
||||
|
||||
// Contains the vertices of the mesh.
|
||||
|
@ -431,6 +446,21 @@ void OdmOrthoPhoto::createOrthoPhoto()
|
|||
log_ << '\n';
|
||||
log_ << "Writing ortho photo to " << outputFile_ << "\n";
|
||||
cv::imwrite(outputFile_, photo_);
|
||||
|
||||
if (!outputCornerFile_.empty())
|
||||
{
|
||||
log_ << "Writing corner coordinates to " << outputCornerFile_ << "\n";
|
||||
std::ofstream cornerStream(outputCornerFile_.c_str());
|
||||
if (!cornerStream.is_open())
|
||||
{
|
||||
throw OdmOrthoPhotoException("Failed opening output corner file " + outputCornerFile_ + ".");
|
||||
}
|
||||
cornerStream.setf(std::ios::scientific, std::ios::floatfield);
|
||||
cornerStream.precision(17);
|
||||
cornerStream << xMin << " " << yMin << " " << xMax << " " << yMax;
|
||||
cornerStream.close();
|
||||
}
|
||||
|
||||
log_ << "Orthophoto generation done.\n";
|
||||
}
|
||||
|
||||
|
@ -443,34 +473,34 @@ void OdmOrthoPhoto::adjustBoundsForGeoRef()
|
|||
|
||||
// The system name
|
||||
std::string system;
|
||||
// The false easting and northing
|
||||
int falseEasting, falseNorthing;
|
||||
// The east and north offsets
|
||||
int eastOffset, northOffset;
|
||||
|
||||
// Parse file
|
||||
std::getline(geoRefStream, system);
|
||||
if(!(geoRefStream >> falseEasting))
|
||||
if(!(geoRefStream >> eastOffset))
|
||||
{
|
||||
throw OdmOrthoPhotoException("Could not extract geographical reference system from \n" + inputGeoRefFile_ + "\nCould not extract false easting.");
|
||||
throw OdmOrthoPhotoException("Could not extract geographical reference system from \n" + inputGeoRefFile_ + "\nCould not extract east offset.");
|
||||
}
|
||||
if(!(geoRefStream >> falseNorthing))
|
||||
if(!(geoRefStream >> northOffset))
|
||||
{
|
||||
throw OdmOrthoPhotoException("Could not extract geographical reference system from \n" + inputGeoRefFile_ + "\nCould not extract false northing.");
|
||||
throw OdmOrthoPhotoException("Could not extract geographical reference system from \n" + inputGeoRefFile_ + "\nCould not extract north offset.");
|
||||
}
|
||||
|
||||
log_ << "Georeference system\n";
|
||||
log_ << "Georeference system:\n";
|
||||
log_ << system << "\n";
|
||||
log_ << "False easting " << falseEasting << "\n";
|
||||
log_ << "False northing " << falseNorthing << "\n";
|
||||
log_ << "East offset: " << eastOffset << "\n";
|
||||
log_ << "North offset: " << northOffset << "\n";
|
||||
|
||||
// Adjust boundary points.
|
||||
boundaryPoint1_[0] = static_cast<float>(worldPoint1_.eastInteger_ - falseEasting) + worldPoint1_.eastFractional_;
|
||||
boundaryPoint1_[1] = static_cast<float>(worldPoint1_.northInteger_ - falseNorthing) + worldPoint1_.northFractional_;
|
||||
boundaryPoint2_[0] = static_cast<float>(worldPoint2_.eastInteger_ - falseEasting) + worldPoint2_.eastFractional_;
|
||||
boundaryPoint2_[1] = static_cast<float>(worldPoint2_.northInteger_ - falseNorthing) + worldPoint2_.northFractional_;
|
||||
boundaryPoint3_[0] = static_cast<float>(worldPoint3_.eastInteger_ - falseEasting) + worldPoint3_.eastFractional_;
|
||||
boundaryPoint3_[1] = static_cast<float>(worldPoint3_.northInteger_ - falseNorthing) + worldPoint3_.northFractional_;
|
||||
boundaryPoint4_[0] = static_cast<float>(worldPoint4_.eastInteger_ - falseEasting) + worldPoint4_.eastFractional_;
|
||||
boundaryPoint4_[1] = static_cast<float>(worldPoint4_.northInteger_ - falseNorthing) + worldPoint4_.northFractional_;
|
||||
boundaryPoint1_[0] = static_cast<float>(worldPoint1_.eastInteger_ - eastOffset) + worldPoint1_.eastFractional_;
|
||||
boundaryPoint1_[1] = static_cast<float>(worldPoint1_.northInteger_ - northOffset) + worldPoint1_.northFractional_;
|
||||
boundaryPoint2_[0] = static_cast<float>(worldPoint2_.eastInteger_ - eastOffset) + worldPoint2_.eastFractional_;
|
||||
boundaryPoint2_[1] = static_cast<float>(worldPoint2_.northInteger_ - northOffset) + worldPoint2_.northFractional_;
|
||||
boundaryPoint3_[0] = static_cast<float>(worldPoint3_.eastInteger_ - eastOffset) + worldPoint3_.eastFractional_;
|
||||
boundaryPoint3_[1] = static_cast<float>(worldPoint3_.northInteger_ - northOffset) + worldPoint3_.northFractional_;
|
||||
boundaryPoint4_[0] = static_cast<float>(worldPoint4_.eastInteger_ - eastOffset) + worldPoint4_.eastFractional_;
|
||||
boundaryPoint4_[1] = static_cast<float>(worldPoint4_.northInteger_ - northOffset) + worldPoint4_.northFractional_;
|
||||
|
||||
log_ << "Local boundary points:\n";
|
||||
log_ << "Point 1: " << boundaryPoint1_[0] << " " << boundaryPoint1_[1] << "\n";
|
||||
|
@ -908,7 +938,7 @@ void OdmOrthoPhoto::renderPixel(int row, int col, float s, float t, const cv::Ma
|
|||
b += static_cast<float>(bl[0]) * dr * dt;
|
||||
b += static_cast<float>(br[0]) * dl * dt;
|
||||
|
||||
photo_.at<cv::Vec3b>(row,col) = cv::Vec3b(static_cast<unsigned char>(b), static_cast<unsigned char>(g), static_cast<unsigned char>(r));
|
||||
photo_.at<cv::Vec4b>(row,col) = cv::Vec4b(static_cast<unsigned char>(b), static_cast<unsigned char>(g), static_cast<unsigned char>(r), 255);
|
||||
}
|
||||
|
||||
void OdmOrthoPhoto::getBarycentricCoordiantes(pcl::PointXYZ v1, pcl::PointXYZ v2, pcl::PointXYZ v3, float x, float y, float &l1, float &l2, float &l3) const
|
||||
|
|
|
@ -177,6 +177,7 @@ private:
|
|||
std::string inputFile_; /**< Path to the textured mesh as an obj-file. */
|
||||
std::string inputGeoRefFile_; /**< Path to the georeference system file. */
|
||||
std::string outputFile_; /**< Path to the destination file. */
|
||||
std::string outputCornerFile_; /**< Path to the output corner file. */
|
||||
std::string logFile_; /**< Path to the log file. */
|
||||
|
||||
float resolution_; /**< The number of pixels per meter in the ortho photo. */
|
||||
|
|
Plik diff jest za duży
Load Diff
Ładowanie…
Reference in New Issue