kopia lustrzana https://github.com/OpenDroneMap/NodeODM
Refactored Task.js to move postprocessing code in a bash script for ease of maintenance, fix existing problem with failing tasks when assets are not in the right place
rodzic
2a9591347b
commit
1deff5ec6c
84
libs/Task.js
84
libs/Task.js
|
@ -286,58 +286,23 @@ module.exports = class Task{
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleProcessExit = (done) => {
|
const runPostProcessingScript = () => {
|
||||||
return (err, code, signal) => {
|
return (done) => {
|
||||||
|
this.runningProcesses.push(
|
||||||
|
processRunner.runPostProcessingScript({
|
||||||
|
projectFolderPath: this.getProjectFolderPath()
|
||||||
|
}, (err, code, signal) => {
|
||||||
if (err) done(err);
|
if (err) done(err);
|
||||||
else{
|
else{
|
||||||
// Don't evaluate if we caused the process to exit via SIGINT?
|
|
||||||
if (code === 0) done();
|
if (code === 0) done();
|
||||||
else done(new Error(`Process exited with code ${code}`));
|
else done(new Error(`Process exited with code ${code}`));
|
||||||
}
|
}
|
||||||
};
|
}, output => {
|
||||||
};
|
|
||||||
|
|
||||||
const handleOutput = output => {
|
|
||||||
this.output.push(output);
|
this.output.push(output);
|
||||||
};
|
})
|
||||||
|
);
|
||||||
const generateTiles = (inputFile, outputDir) => {
|
|
||||||
return (done) => {
|
|
||||||
const inputFilePath = path.join(this.getProjectFolderPath(), inputFile);
|
|
||||||
|
|
||||||
// Not all datasets generate an orthophoto, so we skip
|
|
||||||
// tiling if the orthophoto is missing
|
|
||||||
if (fs.existsSync(inputFilePath)){
|
|
||||||
this.runningProcesses.push(processRunner.runTiler({
|
|
||||||
zoomLevels: "12-21",
|
|
||||||
inputFile: inputFilePath,
|
|
||||||
outputDir: path.join(this.getProjectFolderPath(), outputDir)
|
|
||||||
}, handleProcessExit(done), handleOutput));
|
|
||||||
}else{
|
|
||||||
handleOutput(`${inputFilePath} file not found, skipping tiles generation\n`);
|
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
const generatePotreeCloud = (inputFile, outputDir) => {
|
|
||||||
return (done) => {
|
|
||||||
this.runningProcesses.push(processRunner.runPotreeConverter({
|
|
||||||
inputFile: path.join(this.getProjectFolderPath(), inputFile),
|
|
||||||
outputDir: path.join(this.getProjectFolderPath(), outputDir)
|
|
||||||
}, handleProcessExit(done), handleOutput));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const pdalTranslate = (inputPath, outputPath, filters) => {
|
|
||||||
return (done) => {
|
|
||||||
this.runningProcesses.push(processRunner.runPdalTranslate({
|
|
||||||
inputFile: inputPath,
|
|
||||||
outputFile: outputPath,
|
|
||||||
filters: filters
|
|
||||||
}, handleProcessExit(done), handleOutput));
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// All paths are relative to the project directory (./data/<uuid>/)
|
// All paths are relative to the project directory (./data/<uuid>/)
|
||||||
let allFolders = ['odm_orthophoto', 'odm_georeferencing', 'odm_texturing', 'odm_meshing', 'orthophoto_tiles', 'potree_pointcloud'];
|
let allFolders = ['odm_orthophoto', 'odm_georeferencing', 'odm_texturing', 'odm_meshing', 'orthophoto_tiles', 'potree_pointcloud'];
|
||||||
|
@ -351,35 +316,10 @@ module.exports = class Task{
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let orthophotoPath = path.join('odm_orthophoto', 'odm_orthophoto.tif'),
|
async.series([
|
||||||
lasPointCloudPath = path.join('odm_georeferencing', 'odm_georeferenced_model.las'),
|
runPostProcessingScript(),
|
||||||
plyPointCloudPath = path.join('odm_georeferencing', 'odm_georeferenced_model.ply'),
|
|
||||||
projectFolderPath = this.getProjectFolderPath();
|
|
||||||
|
|
||||||
let commands = [
|
|
||||||
generateTiles(orthophotoPath, 'orthophoto_tiles'),
|
|
||||||
generatePotreeCloud(plyPointCloudPath, 'potree_pointcloud'),
|
|
||||||
createZipArchive('all.zip', allFolders)
|
createZipArchive('all.zip', allFolders)
|
||||||
];
|
], (err) => {
|
||||||
|
|
||||||
// If point cloud file does not exist, it's likely because location (GPS/GPC) information
|
|
||||||
// was missing and the file was not generated.
|
|
||||||
let fullPlyPointCloudPath = path.join(projectFolderPath, plyPointCloudPath);
|
|
||||||
if (!fs.existsSync(fullPlyPointCloudPath)){
|
|
||||||
let unreferencedPointCloudPath = path.join(projectFolderPath, "opensfm", "depthmaps", "merged.ply");
|
|
||||||
if (fs.existsSync(unreferencedPointCloudPath)){
|
|
||||||
logger.info(`${plyPointCloudPath} is missing, will attempt to generate it from ${unreferencedPointCloudPath}`);
|
|
||||||
commands.unshift(pdalTranslate(unreferencedPointCloudPath, fullPlyPointCloudPath, [
|
|
||||||
{
|
|
||||||
// opensfm's ply files map colors with the diffuse_ prefix
|
|
||||||
dimensions: "diffuse_red = red, diffuse_green = green, diffuse_blue = blue",
|
|
||||||
type: "filters.ferry"
|
|
||||||
}
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async.series(commands, (err) => {
|
|
||||||
if (!err){
|
if (!err){
|
||||||
this.setStatus(statusCodes.COMPLETED);
|
this.setStatus(statusCodes.COMPLETED);
|
||||||
finished();
|
finished();
|
||||||
|
|
|
@ -69,42 +69,10 @@ function makeRunner(command, args, requiredOptions = [], outputTestFile = null){
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
runTiler: makeRunner("gdal2tiles.py",
|
runPostProcessingScript: makeRunner(os.path.join(__dirname, "..", "scripts", "postprocess.sh"),
|
||||||
function(options){
|
function(options){
|
||||||
return ["-z", options.zoomLevels,
|
return [options.projectFolderPath];
|
||||||
"-n",
|
|
||||||
"-w", "none",
|
|
||||||
options.inputFile,
|
|
||||||
options.outputDir
|
|
||||||
];
|
|
||||||
},
|
},
|
||||||
["zoomLevels", "inputFile", "outputDir"],
|
["projectFolderPath"])
|
||||||
path.join("..", "tests", "gdal2tiles_output.txt")),
|
|
||||||
|
|
||||||
runPotreeConverter: makeRunner("PotreeConverter",
|
|
||||||
function(options){
|
|
||||||
return [options.inputFile,
|
|
||||||
"-o", options.outputDir];
|
|
||||||
},
|
|
||||||
["inputFile", "outputDir"],
|
|
||||||
path.join("..", "tests", "potree_output.txt")),
|
|
||||||
|
|
||||||
runPdalTranslate: makeRunner("/code/SuperBuild/build/pdal/bin/pdal",
|
|
||||||
function(options){
|
|
||||||
let opts = ["translate",
|
|
||||||
"-i", options.inputFile,
|
|
||||||
"-o", options.outputFile];
|
|
||||||
|
|
||||||
if (options.filters){
|
|
||||||
opts = opts.concat([
|
|
||||||
"--json",
|
|
||||||
JSON.stringify(options.filters)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return opts;
|
|
||||||
},
|
|
||||||
["inputFile", "outputFile"])
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This file executes the post-processing steps for a task after a dataset
|
||||||
|
# has been processed by OpenDroneMap. It generates derivative computations.
|
||||||
|
#
|
||||||
|
# As a general rule, post-processing commands should never fail the task
|
||||||
|
#(for example, if a point cloud could not be generated, the PotreeConverter
|
||||||
|
# step will fail, but the script should still continue processing the rest and
|
||||||
|
# return a 0 code). The idea is to post-process as much as possible, knowing
|
||||||
|
# that some parts might fail and that partial results should be returned in such cases.
|
||||||
|
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
echo "Usage: $0 <projectPath>"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Switch to project path folder (data/<uuid>/)
|
||||||
|
cd "$(dirname "$0")/../$1"
|
||||||
|
echo "Postprocessing: $(pwd)"
|
||||||
|
|
||||||
|
# Generate Tiles
|
||||||
|
if hash gdal2tiles.py 2>/dev/null; then
|
||||||
|
orthophoto_path="odm_orthophoto/odm_orthophoto.tif"
|
||||||
|
|
||||||
|
if [ -e "$orthophoto_path" ]; then
|
||||||
|
gdal2tiles.py -z 12-21 -n -w none $orthophoto_path orthophoto_tiles
|
||||||
|
else
|
||||||
|
echo "No orthophoto found at $orthophoto_path: will skip tiling"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "gdal2tiles.py is not installed, will skip tiling"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate Potree point cloud (if PotreeConverter is available)
|
||||||
|
if hash PotreeConverter 2>/dev/null; then
|
||||||
|
potree_input_path=""
|
||||||
|
for path in "odm_georeferencing/odm_georeferenced_model.ply" \
|
||||||
|
"opensfm/depthmaps/merged.ply" \
|
||||||
|
"pmvs/recon0/models/option-0000.ply"; do
|
||||||
|
if [ -e $path ]; then
|
||||||
|
echo "Found suitable point cloud for PotreeConverter: $path"
|
||||||
|
potree_input_path=$path
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ! -z "$potree_input_path" ]; then
|
||||||
|
PotreeConverter $potree_input_path -o potree_pointcloud
|
||||||
|
else
|
||||||
|
echo "Potree point cloud will not be generated (no suitable input files found)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "PotreeConverter is not installed, will skip generation of Potree point cloud"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Postprocessing: done (•̀ᴗ•́)و!"
|
||||||
|
exit 0
|
|
@ -0,0 +1,4 @@
|
||||||
|
h=""
|
||||||
|
if [ ! -z "$h" ]; then
|
||||||
|
echo "OK"
|
||||||
|
fi
|
Ładowanie…
Reference in New Issue