kopia lustrzana https://github.com/OpenDroneMap/ODM
Merge branch 'master' into default-opensfm
commit
65fab9fccc
47
README.md
47
README.md
|
@ -23,7 +23,7 @@ So far, it does Point Clouds, Digital Surface Models, Textured Digital Surface M
|
||||||
|
|
||||||
## QUICKSTART
|
## QUICKSTART
|
||||||
|
|
||||||
Requires Ubuntu 14.04 or later, see https://github.com/OpenDroneMap/odm_vagrant for running on Windows in a VM
|
OpenDroneMap can run natively on Ubuntu 14.04 or later, see [Build and Run Using Docker](#build-and-run-using-docker) for running on Windows / MacOS. A Vagrant VM is also available: https://github.com/OpenDroneMap/odm_vagrant.
|
||||||
|
|
||||||
*Support for Ubuntu 12.04 is currently BROKEN with the addition of OpenSfM and Ceres-Solver. It is likely to remain broken unless a champion is found to fix it.*
|
*Support for Ubuntu 12.04 is currently BROKEN with the addition of OpenSfM and Ceres-Solver. It is likely to remain broken unless a champion is found to fix it.*
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ Current version: 0.2 (this software is in beta)
|
||||||
- textured mesh model: odm_texturing/odm_textured_model_geo.obj
|
- textured mesh model: odm_texturing/odm_textured_model_geo.obj
|
||||||
- point cloud (georeferenced): odm_georeferencing/odm_georeferenced_model.ply
|
- point cloud (georeferenced): odm_georeferencing/odm_georeferenced_model.ply
|
||||||
|
|
||||||
See below for more detailed installation instructions.
|
See [here](https://github.com/OpenDroneMap/OpenDroneMap/tree/ebaaf802a1fb50e335b3807a35d00cba1e106d11#installation) for more detailed installation instructions.
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
|
@ -65,22 +65,13 @@ Note that using `run.sh` sets these temporarily in the shell.
|
||||||
|
|
||||||
### Run OpenDroneMap
|
### Run OpenDroneMap
|
||||||
|
|
||||||
First you need a set of images, taken from a drone or otherwise.
|
First you need a set of images, taken from a drone or otherwise. Example data can be cloned from https://github.com/OpenDroneMap/odm_data
|
||||||
|
|
||||||
Create a project folder and places your images in an "images" directory:
|
|
||||||
|
|
||||||
|-- /path/to/project/
|
|
||||||
|-- images/
|
|
||||||
|-- img-1234.jpg
|
|
||||||
|-- ...
|
|
||||||
|
|
||||||
Example data can be cloned from https://github.com/OpenDroneMap/odm_data
|
|
||||||
|
|
||||||
Then run:
|
Then run:
|
||||||
|
|
||||||
python run.py --project-path /path/to/project
|
python run.py --project-path /path/to/project -i /path/to/images
|
||||||
|
|
||||||
There are many options for tuning your project. See the [wiki](https://github.com/OpenDroneMap/OpenDroneMap/wiki/Run-Time-Parameters) or run `python run.py -h`
|
The images will be copied over to the project path so you only need to specify the `-i /path/` once. There are many options for tuning your project. See the [wiki](https://github.com/OpenDroneMap/OpenDroneMap/wiki/Run-Time-Parameters) or run `python run.py -h`
|
||||||
|
|
||||||
### View Results
|
### View Results
|
||||||
|
|
||||||
|
@ -121,7 +112,7 @@ Any file ending in .obj or .ply can be opened and viewed in [MeshLab](http://mes
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
You can also view the orthophoto GeoTIFF in QGIS or other mapping software:
|
You can also view the orthophoto GeoTIFF in [QGIS](http://www.qgis.org/) or other mapping software:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
@ -133,16 +124,16 @@ has equivalent procedures for Mac OS X and Windows. See [docs.docker.com](docs.d
|
||||||
OpenDroneMap is Dockerized, meaning you can use containerization to build and run it without tampering with the configuration of libraries and packages already
|
OpenDroneMap is Dockerized, meaning you can use containerization to build and run it without tampering with the configuration of libraries and packages already
|
||||||
installed on your machine. Docker software is free to install and use in this context. If you don't have it installed,
|
installed on your machine. Docker software is free to install and use in this context. If you don't have it installed,
|
||||||
see the [Docker Ubuntu installation tutorial](https://docs.docker.com/engine/installation/linux/ubuntulinux/) and follow the
|
see the [Docker Ubuntu installation tutorial](https://docs.docker.com/engine/installation/linux/ubuntulinux/) and follow the
|
||||||
instructions through "Create a Docker group". Once Docker is installed, an OpenDroneMap Docker image can be created
|
instructions through "Create a Docker group". Once Docker is installed, the fastest way to use OpenDroneMap is to run a pre-built image by typing:
|
||||||
like so:
|
|
||||||
|
docker run -it --rm -v $(pwd)/images:/code/images -v $(pwd)/odm_orthophoto:/code/odm_orthophoto -v $(pwd)/odm_texturing:/code/odm_texturing opendronemap/opendronemap
|
||||||
|
|
||||||
|
If you want to build your own Docker image from sources, type:
|
||||||
|
|
||||||
docker build -t packages -f packages.Dockerfile .
|
docker build -t packages -f packages.Dockerfile .
|
||||||
docker build -t odm_image .
|
|
||||||
docker run -it --user root\
|
docker build -t my_odm_image .
|
||||||
-v $(pwd)/images:/code/images\
|
docker run -it --rm -v $(pwd)/images:/code/images v $(pwd)/odm_orthophoto:/code/odm_orthophoto -v $(pwd)/odm_texturing:/code/odm_texturing my_odm_image
|
||||||
-v $(pwd)/odm_orthophoto:/code/odm_orthophoto\
|
|
||||||
-v $(pwd)/odm_texturing:/code/odm_texturing\
|
|
||||||
--rm odm_image
|
|
||||||
|
|
||||||
Using this method, the containerized ODM will process the images in the OpenDroneMap/images directory and output results
|
Using this method, the containerized ODM will process the images in the OpenDroneMap/images directory and output results
|
||||||
to the OpenDroneMap/odm_orthophoto and OpenDroneMap/odm_texturing directories as described in the **Viewing Results** section.
|
to the OpenDroneMap/odm_orthophoto and OpenDroneMap/odm_texturing directories as described in the **Viewing Results** section.
|
||||||
|
@ -150,13 +141,11 @@ If you want to view other results outside the Docker image simply add which dire
|
||||||
established above. For example, if you're interested in the dense cloud results generated by PMVS and in the orthophoto,
|
established above. For example, if you're interested in the dense cloud results generated by PMVS and in the orthophoto,
|
||||||
simply use the following `docker run` command after building the image:
|
simply use the following `docker run` command after building the image:
|
||||||
|
|
||||||
docker run -it \
|
docker run -it --rm -v $(pwd)/images:/code/images -v $(pwd)/pmvs:/code/pmvs -v $(pwd)/odm_orthophoto:/code/odm_orthophoto my_odm_image
|
||||||
-v $(pwd)/images:/code/images\
|
|
||||||
-v $(pwd)/pmvs:/code/pmvs\
|
|
||||||
-v $(pwd)/odm_orthophoto:/code/odm_orthophoto\
|
|
||||||
--rm odm_image
|
|
||||||
|
|
||||||
To pass in custom parameters to the run.py script, simply pass it as arguments to the `docker run` command.
|
To pass in custom parameters to the run.py script, simply pass it as arguments to the `docker run` command. For example:
|
||||||
|
|
||||||
|
docker run -it --rm -v $(pwd)/images:/code/images v $(pwd)/odm_orthophoto:/code/odm_orthophoto -v $(pwd)/odm_texturing:/code/odm_texturing opendronemap/opendronemap --resize-to 1800 --force-ccd 6.16
|
||||||
|
|
||||||
## User Interface
|
## User Interface
|
||||||
|
|
||||||
|
|
|
@ -1141,9 +1141,40 @@ void Georef::createGeoreferencedModelFromExifData()
|
||||||
|
|
||||||
void Georef::chooseBestGCPTriplet(size_t &gcp0, size_t &gcp1, size_t &gcp2)
|
void Georef::chooseBestGCPTriplet(size_t &gcp0, size_t &gcp1, size_t &gcp2)
|
||||||
{
|
{
|
||||||
double minTotError = std::numeric_limits<double>::infinity();
|
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_)));
|
||||||
|
}
|
||||||
|
|
||||||
for(size_t t = 0; t < gcps_.size(); ++t)
|
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_)
|
if (gcps_[t].use_)
|
||||||
{
|
{
|
||||||
|
@ -1180,14 +1211,47 @@ void Georef::chooseBestGCPTriplet(size_t &gcp0, size_t &gcp1, size_t &gcp2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log_ << "Mean georeference error " << minTotError / static_cast<double>(cameras_.size()) << '\n';
|
|
||||||
|
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)
|
void Georef::chooseBestCameraTriplet(size_t &cam0, size_t &cam1, size_t &cam2)
|
||||||
{
|
{
|
||||||
double minTotError = std::numeric_limits<double>::infinity();
|
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_)));
|
||||||
|
}
|
||||||
|
|
||||||
for(size_t t = 0; t < cameras_.size(); ++t)
|
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 s = t; s < cameras_.size(); ++s)
|
||||||
{
|
{
|
||||||
|
@ -1216,7 +1280,8 @@ void Georef::chooseBestCameraTriplet(size_t &cam0, size_t &cam1, size_t &cam2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log_ << "Mean georeference error " << minTotError / static_cast<double>(cameras_.size()) << '\n';
|
log_ << '[' << offset+1 << " of " << stride << "] Mean georeference error " << minTotError / static_cast<double>(cameras_.size());
|
||||||
|
log_ << " (" << cam0 << ", " << cam1 << ", " << cam2 << ")\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void Georef::printGeorefSystem()
|
void Georef::printGeorefSystem()
|
||||||
|
|
|
@ -111,6 +111,17 @@ struct GeorefCamera
|
||||||
friend std::ostream& operator<<(std::ostream &os, const GeorefCamera &cam);
|
friend std::ostream& operator<<(std::ostream &os, const GeorefCamera &cam);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The GeorefBestTriplet struct is used to store the best triplet found.
|
||||||
|
*/
|
||||||
|
struct GeorefBestTriplet
|
||||||
|
{
|
||||||
|
size_t t_; /**< First ordinate of the best triplet found. **/
|
||||||
|
size_t s_; /**< Second ordinate of the best triplet found. **/
|
||||||
|
size_t p_; /**< Third ordinate of the best triplet found. **/
|
||||||
|
double err_; /**< Error of this triplet. **/
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief The Georef class is used to transform a mesh into a georeferenced system.
|
* \brief The Georef class is used to transform a mesh into a georeferenced system.
|
||||||
* The class reads camera positions from a bundle file.
|
* The class reads camera positions from a bundle file.
|
||||||
|
@ -196,11 +207,21 @@ private:
|
||||||
*/
|
*/
|
||||||
void chooseBestGCPTriplet(size_t &gcp0, size_t &gcp1, size_t &gcp2);
|
void chooseBestGCPTriplet(size_t &gcp0, size_t &gcp1, size_t &gcp2);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief findBestGCPTriplet Partitioned version of chooseBestGCPTriplet.
|
||||||
|
*/
|
||||||
|
void findBestGCPTriplet(size_t &gcp0, size_t &gcp1, size_t &gcp2, size_t offset, size_t stride, double &minTotError);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief chooseBestCameraTriplet Chooses the best triplet of cameras to use when making the model georeferenced.
|
* \brief chooseBestCameraTriplet Chooses the best triplet of cameras to use when making the model georeferenced.
|
||||||
*/
|
*/
|
||||||
void chooseBestCameraTriplet(size_t &cam0, size_t &cam1, size_t &cam2);
|
void chooseBestCameraTriplet(size_t &cam0, size_t &cam1, size_t &cam2);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief findBestCameraTriplet Partitioned version of chooseBestCameraTriplet.
|
||||||
|
*/
|
||||||
|
void findBestCameraTriplet(size_t &cam0, size_t &cam1, size_t &cam2, size_t offset, size_t stride, double &minTotError);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief printGeorefSystem Prints a file containing information about the georeference system, next to the ouptut file.
|
* \brief printGeorefSystem Prints a file containing information about the georeference system, next to the ouptut file.
|
||||||
**/
|
**/
|
||||||
|
|
|
@ -16,6 +16,10 @@ parser = argparse.ArgumentParser(description='OpenDroneMap')
|
||||||
|
|
||||||
|
|
||||||
def config():
|
def config():
|
||||||
|
parser.add_argument('--images', '-i',
|
||||||
|
metavar='<string>',
|
||||||
|
help='Path to input images'),
|
||||||
|
|
||||||
parser.add_argument('--project-path',
|
parser.add_argument('--project-path',
|
||||||
metavar='<string>',
|
metavar='<string>',
|
||||||
help='Path to the project to process')
|
help='Path to the project to process')
|
||||||
|
@ -118,6 +122,13 @@ def config():
|
||||||
'images based on GPS exif data. Set to 0 to skip '
|
'images based on GPS exif data. Set to 0 to skip '
|
||||||
'pre-matching. Default: %(default)s')
|
'pre-matching. Default: %(default)s')
|
||||||
|
|
||||||
|
parser.add_argument('--opensfm-processes',
|
||||||
|
metavar='<positive integer>',
|
||||||
|
default=context.num_cores,
|
||||||
|
type=int,
|
||||||
|
help=('The maximum number of processes to use in dense '
|
||||||
|
'reconstruction. Default: %(default)s'))
|
||||||
|
|
||||||
parser.add_argument('--use-pmvs',
|
parser.add_argument('--use-pmvs',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=False,
|
default=False,
|
||||||
|
@ -166,7 +177,7 @@ def config():
|
||||||
'value leads to more stable reconstructions, but '
|
'value leads to more stable reconstructions, but '
|
||||||
'the program becomes slower. Default: %(default)s')
|
'the program becomes slower. Default: %(default)s')
|
||||||
|
|
||||||
parser.add_argument('--pmvs-minImageNum',
|
parser.add_argument('--pmvs-min-images',
|
||||||
metavar='<positive integer>',
|
metavar='<positive integer>',
|
||||||
default=3,
|
default=3,
|
||||||
type=int,
|
type=int,
|
||||||
|
@ -181,14 +192,14 @@ def config():
|
||||||
help=('The maximum number of cores to use in dense '
|
help=('The maximum number of cores to use in dense '
|
||||||
'reconstruction. Default: %(default)s'))
|
'reconstruction. Default: %(default)s'))
|
||||||
|
|
||||||
parser.add_argument('--odm_meshing-maxVertexCount',
|
parser.add_argument('--mesh-size',
|
||||||
metavar='<positive integer>',
|
metavar='<positive integer>',
|
||||||
default=100000,
|
default=100000,
|
||||||
type=int,
|
type=int,
|
||||||
help=('The maximum vertex count of the output mesh '
|
help=('The maximum vertex count of the output mesh '
|
||||||
'Default: %(default)s'))
|
'Default: %(default)s'))
|
||||||
|
|
||||||
parser.add_argument('--odm_meshing-octreeDepth',
|
parser.add_argument('--mesh-octree-depth',
|
||||||
metavar='<positive integer>',
|
metavar='<positive integer>',
|
||||||
default=9,
|
default=9,
|
||||||
type=int,
|
type=int,
|
||||||
|
@ -196,14 +207,14 @@ def config():
|
||||||
'increase to get more vertices, recommended '
|
'increase to get more vertices, recommended '
|
||||||
'values are 8-12. Default: %(default)s'))
|
'values are 8-12. Default: %(default)s'))
|
||||||
|
|
||||||
parser.add_argument('--odm_meshing-samplesPerNode',
|
parser.add_argument('--mesh-samples',
|
||||||
metavar='<float >= 1.0>',
|
metavar='<float >= 1.0>',
|
||||||
default=1.0,
|
default=1.0,
|
||||||
type=float,
|
type=float,
|
||||||
help=('Number of points per octree node, recommended '
|
help=('Number of points per octree node, recommended '
|
||||||
'and default value: %(default)s'))
|
'and default value: %(default)s'))
|
||||||
|
|
||||||
parser.add_argument('--odm_meshing-solverDivide',
|
parser.add_argument('--mesh-solver-divide',
|
||||||
metavar='<positive integer>',
|
metavar='<positive integer>',
|
||||||
default=9,
|
default=9,
|
||||||
type=int,
|
type=int,
|
||||||
|
@ -213,78 +224,64 @@ def config():
|
||||||
'times slightly but helps reduce memory usage. '
|
'times slightly but helps reduce memory usage. '
|
||||||
'Default: %(default)s'))
|
'Default: %(default)s'))
|
||||||
|
|
||||||
parser.add_argument('--mvs_texturing-dataTerm',
|
parser.add_argument('--texturing-data-term',
|
||||||
metavar='<string>',
|
metavar='<string>',
|
||||||
default='gmi',
|
default='gmi',
|
||||||
help=('Data term: [area, gmi]. Default: %(default)s'))
|
help=('Data term: [area, gmi]. Default: '
|
||||||
|
'%(default)s'))
|
||||||
|
|
||||||
parser.add_argument('--mvs_texturing-outlierRemovalType',
|
parser.add_argument('--texturing-outlier-removal-type',
|
||||||
metavar='<string>',
|
metavar='<string>',
|
||||||
default='none',
|
default='none',
|
||||||
help=('Type of photometric outlier removal method: '
|
help=('Type of photometric outlier removal method: '
|
||||||
'[none, gauss_damping, gauss_clamping]. Default: '
|
'[none, gauss_damping, gauss_clamping]. Default: '
|
||||||
'%(default)s'))
|
'%(default)s'))
|
||||||
|
|
||||||
parser.add_argument('--mvs_texturing-skipGeometricVisibilityTest',
|
parser.add_argument('--texturing-skip-visibility-test',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=False,
|
default=False,
|
||||||
help=('Skip geometric visibility test. Default: %(default)s'))
|
help=('Skip geometric visibility test. Default: '
|
||||||
|
' %(default)s'))
|
||||||
|
|
||||||
parser.add_argument('--mvs_texturing-skipGlobalSeamLeveling',
|
parser.add_argument('--texturing-skip-global-seam-leveling',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=False,
|
default=False,
|
||||||
help=('Skip geometric visibility test. Default: %(default)s'))
|
help=('Skip global seam leveling. Useful for IR data.'
|
||||||
|
'Default: %(default)s'))
|
||||||
|
|
||||||
parser.add_argument('--mvs_texturing-skipLocalSeamLeveling',
|
parser.add_argument('--texturing-skip-local-seam-leveling',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=False,
|
default=False,
|
||||||
help=('Skip local seam blending. Default: %(default)s'))
|
help='Skip local seam blending. Default: %(default)s')
|
||||||
|
|
||||||
parser.add_argument('--mvs_texturing-skipHoleFilling',
|
parser.add_argument('--texturing-skip-hole-filling',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=False,
|
default=False,
|
||||||
help=('Skip filling of holes in the mesh. Default: %(default)s'))
|
help=('Skip filling of holes in the mesh. Default: '
|
||||||
|
' %(default)s'))
|
||||||
|
|
||||||
parser.add_argument('--mvs_texturing-keepUnseenFaces',
|
parser.add_argument('--texturing-keep-unseen-faces',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=False,
|
default=False,
|
||||||
help=('Keep faces in the mesh that are not seen in any camera. '
|
help=('Keep faces in the mesh that are not seen in any camera. '
|
||||||
'Default: %(default)s'))
|
'Default: %(default)s'))
|
||||||
|
|
||||||
# Old odm_texturing arguments
|
parser.add_argument('--gcp',
|
||||||
|
|
||||||
parser.add_argument('--odm_texturing-textureResolution',
|
|
||||||
metavar='<positive integer>',
|
|
||||||
default=4096,
|
|
||||||
type=int,
|
|
||||||
help=('The resolution of the output textures. Must be '
|
|
||||||
'greater than textureWithSize. Default: %(default)s'))
|
|
||||||
|
|
||||||
parser.add_argument('--odm_texturing-textureWithSize',
|
|
||||||
metavar='<positive integer>',
|
|
||||||
default=3600,
|
|
||||||
type=int,
|
|
||||||
help=('The resolution to rescale the images performing '
|
|
||||||
'the texturing. Default: %(default)s'))
|
|
||||||
|
|
||||||
# End of old odm_texturing arguments
|
|
||||||
|
|
||||||
parser.add_argument('--odm_georeferencing-gcpFile',
|
|
||||||
metavar='<path string>',
|
metavar='<path string>',
|
||||||
default='gcp_list.txt',
|
default=None,
|
||||||
help=('path to the file containing the ground control '
|
help=('path to the file containing the ground control '
|
||||||
'points used for georeferencing. Default: '
|
'points used for georeferencing. Default: '
|
||||||
'%(default)s. The file needs to '
|
'%(default)s. The file needs to '
|
||||||
'be on the following line format: \neasting '
|
'be on the following line format: \neasting '
|
||||||
'northing height pixelrow pixelcol imagename'))
|
'northing height pixelrow pixelcol imagename'))
|
||||||
|
|
||||||
parser.add_argument('--odm_georeferencing-useGcp',
|
parser.add_argument('--use-exif',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=False,
|
default=False,
|
||||||
help='Enabling GCPs from the file above. The GCP file '
|
help=('Use this tag if you have a gcp_list.txt but '
|
||||||
'is not used by default.')
|
'want to use the exif geotags instead'))
|
||||||
|
|
||||||
parser.add_argument('--odm_orthophoto-resolution',
|
parser.add_argument('--orthophoto-resolution',
|
||||||
metavar='<float > 0.0>',
|
metavar='<float > 0.0>',
|
||||||
default=20.0,
|
default=20.0,
|
||||||
type=float,
|
type=float,
|
||||||
|
|
|
@ -331,9 +331,13 @@ class ODM_GeoRef(object):
|
||||||
|
|
||||||
|
|
||||||
class ODM_Tree(object):
|
class ODM_Tree(object):
|
||||||
def __init__(self, root_path):
|
def __init__(self, root_path, images_path):
|
||||||
# root path to the project
|
# root path to the project
|
||||||
self.root_path = io.absolute_path_file(root_path)
|
self.root_path = io.absolute_path_file(root_path)
|
||||||
|
if not images_path:
|
||||||
|
self.input_images = io.join_paths(self.root_path, 'images')
|
||||||
|
else:
|
||||||
|
self.input_images = io.absolute_path_file(images_path)
|
||||||
|
|
||||||
# modules paths
|
# modules paths
|
||||||
|
|
||||||
|
|
5
run.py
5
run.py
|
@ -3,6 +3,7 @@
|
||||||
from opendm import log
|
from opendm import log
|
||||||
from opendm import config
|
from opendm import config
|
||||||
from opendm import system
|
from opendm import system
|
||||||
|
from opendm import io
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import ecto
|
import ecto
|
||||||
|
@ -26,8 +27,12 @@ if __name__ == '__main__':
|
||||||
# Force to provide the images path
|
# Force to provide the images path
|
||||||
if args.project_path is None:
|
if args.project_path is None:
|
||||||
usage()
|
usage()
|
||||||
|
elif not io.dir_exists(args.project_path):
|
||||||
|
log.ODM_WARNING('Directory %s does not exist. Creating it now.' % args.project_path)
|
||||||
|
system.mkdir_p(os.path.abspath(args.project_path))
|
||||||
|
|
||||||
#If user asks to rerun everything, delete all of the existing progress directories.
|
#If user asks to rerun everything, delete all of the existing progress directories.
|
||||||
|
# TODO: Move this somewhere it's not hard-coded
|
||||||
if args.rerun_all:
|
if args.rerun_all:
|
||||||
os.system("rm -rf "
|
os.system("rm -rf "
|
||||||
+ args.project_path + "images_resize/ "
|
+ args.project_path + "images_resize/ "
|
||||||
|
|
|
@ -7,6 +7,8 @@ from opendm import context
|
||||||
from opendm import io
|
from opendm import io
|
||||||
from opendm import types
|
from opendm import types
|
||||||
from opendm import log
|
from opendm import log
|
||||||
|
from opendm import system
|
||||||
|
from shutil import copyfile
|
||||||
|
|
||||||
|
|
||||||
def make_odm_photo(force_focal, force_ccd, path_file):
|
def make_odm_photo(force_focal, force_ccd, path_file):
|
||||||
|
@ -28,32 +30,41 @@ class ODMLoadDatasetCell(ecto.Cell):
|
||||||
outputs.declare("photos", "list of ODMPhotos", [])
|
outputs.declare("photos", "list of ODMPhotos", [])
|
||||||
|
|
||||||
def process(self, inputs, outputs):
|
def process(self, inputs, outputs):
|
||||||
# check if the extension is sopported
|
# check if the extension is supported
|
||||||
def supported_extension(file_name):
|
def supported_extension(file_name):
|
||||||
(pathfn, ext) = os.path.splitext(file_name)
|
(pathfn, ext) = os.path.splitext(file_name)
|
||||||
return ext.lower() in context.supported_extensions
|
return ext.lower() in context.supported_extensions
|
||||||
|
|
||||||
|
# Get supported images from dir
|
||||||
|
def get_images(in_dir):
|
||||||
|
# filter images for its extension type
|
||||||
|
log.ODM_DEBUG(in_dir)
|
||||||
|
return [f for f in io.get_files_list(in_dir) if supported_extension(f)]
|
||||||
|
|
||||||
log.ODM_INFO('Running ODM Load Dataset Cell')
|
log.ODM_INFO('Running ODM Load Dataset Cell')
|
||||||
|
|
||||||
# get inputs
|
# get inputs
|
||||||
tree = self.inputs.tree
|
tree = self.inputs.tree
|
||||||
|
|
||||||
# set images directory
|
# get images directory
|
||||||
images_dir = tree.dataset_resize
|
input_dir = tree.input_images
|
||||||
|
|
||||||
if not io.dir_exists(images_dir):
|
|
||||||
images_dir = tree.dataset_raw
|
images_dir = tree.dataset_raw
|
||||||
|
resize_dir = tree.dataset_resize
|
||||||
|
|
||||||
|
# Check first if a project already exists. This is a mediocre way to check, by checking the resize dir
|
||||||
|
if io.dir_exists(resize_dir):
|
||||||
|
log.ODM_DEBUG("resize dir: %s" % resize_dir)
|
||||||
|
images_dir = resize_dir
|
||||||
|
# if first time running, create project directory and copy images over to project/images
|
||||||
|
else:
|
||||||
if not io.dir_exists(images_dir):
|
if not io.dir_exists(images_dir):
|
||||||
log.ODM_ERROR("You must put your pictures into an <images> directory")
|
log.ODM_INFO("Project directory %s doesn't exist. Creating it now. " % images_dir)
|
||||||
return ecto.QUIT
|
system.mkdir_p(images_dir)
|
||||||
|
copied = [copyfile(io.join_paths(input_dir, f), io.join_paths(images_dir, f)) for f in get_images(input_dir)]
|
||||||
|
|
||||||
log.ODM_DEBUG('Loading dataset from: %s' % images_dir)
|
log.ODM_DEBUG('Loading dataset from: %s' % images_dir)
|
||||||
|
|
||||||
# find files in the given directory
|
files = get_images(images_dir)
|
||||||
files = io.get_files_list(images_dir)
|
|
||||||
|
|
||||||
# filter images for its extension type
|
|
||||||
files = [f for f in files if supported_extension(f)]
|
|
||||||
|
|
||||||
if files:
|
if files:
|
||||||
# create ODMPhoto list
|
# create ODMPhoto list
|
||||||
|
|
|
@ -3,7 +3,6 @@ import os
|
||||||
|
|
||||||
from opendm import context
|
from opendm import context
|
||||||
from opendm import types
|
from opendm import types
|
||||||
from opendm import config
|
|
||||||
from opendm import io
|
from opendm import io
|
||||||
from opendm import system
|
from opendm import system
|
||||||
|
|
||||||
|
@ -45,7 +44,7 @@ class ODMApp(ecto.BlackBox):
|
||||||
'opensfm': ODMOpenSfMCell(use_exif_size=False,
|
'opensfm': ODMOpenSfMCell(use_exif_size=False,
|
||||||
feature_process_size=p.args.resize_to,
|
feature_process_size=p.args.resize_to,
|
||||||
feature_min_frames=p.args.min_num_features,
|
feature_min_frames=p.args.min_num_features,
|
||||||
processes=context.num_cores,
|
processes=p.args.opensfm_processes,
|
||||||
matching_gps_neighbors=p.args.matcher_neighbors,
|
matching_gps_neighbors=p.args.matcher_neighbors,
|
||||||
matching_gps_distance=p.args.matcher_distance),
|
matching_gps_distance=p.args.matcher_distance),
|
||||||
'slam': ODMSlamCell(),
|
'slam': ODMSlamCell(),
|
||||||
|
@ -54,36 +53,32 @@ class ODMApp(ecto.BlackBox):
|
||||||
csize=p.args.pmvs_csize,
|
csize=p.args.pmvs_csize,
|
||||||
thresh=p.args.pmvs_threshold,
|
thresh=p.args.pmvs_threshold,
|
||||||
wsize=p.args.pmvs_wsize,
|
wsize=p.args.pmvs_wsize,
|
||||||
min_imgs=p.args.pmvs_minImageNum,
|
min_imgs=p.args.pmvs_min_images,
|
||||||
cores=p.args.pmvs_num_cores),
|
cores=p.args.pmvs_num_cores),
|
||||||
'meshing': ODMeshingCell(max_vertex=p.args.odm_meshing_maxVertexCount,
|
'meshing': ODMeshingCell(max_vertex=p.args.mesh_size,
|
||||||
oct_tree=p.args.odm_meshing_octreeDepth,
|
oct_tree=p.args.mesh_octree_depth,
|
||||||
samples=p.args.odm_meshing_samplesPerNode,
|
samples=p.args.mesh_samples,
|
||||||
solver=p.args.odm_meshing_solverDivide,
|
solver=p.args.mesh_solver_divide,
|
||||||
verbose=p.args.verbose),
|
verbose=p.args.verbose),
|
||||||
'texturing': ODMMvsTexCell(data_term=p.args.mvs_texturing_dataTerm,
|
'texturing': ODMMvsTexCell(data_term=p.args.texturing_data_term,
|
||||||
outlier_rem_type=p.args.mvs_texturing_outlierRemovalType,
|
outlier_rem_type=p.args.texturing_outlier_removal_type,
|
||||||
skip_vis_test=p.args.mvs_texturing_skipGeometricVisibilityTest,
|
skip_vis_test=p.args.texturing_skip_visibility_test,
|
||||||
skip_glob_seam_leveling=p.args.mvs_texturing_skipGlobalSeamLeveling,
|
skip_glob_seam_leveling=p.args.texturing_skip_global_seam_leveling,
|
||||||
skip_loc_seam_leveling=p.args.mvs_texturing_skipLocalSeamLeveling,
|
skip_loc_seam_leveling=p.args.texturing_skip_local_seam_leveling,
|
||||||
skip_hole_fill=p.args.mvs_texturing_skipHoleFilling,
|
skip_hole_fill=p.args.texturing_skip_hole_filling,
|
||||||
keep_unseen_faces=p.args.mvs_texturing_keepUnseenFaces),
|
keep_unseen_faces=p.args.texturing_keep_unseen_faces),
|
||||||
# Old odm_texturing
|
|
||||||
# 'texturing': ODMTexturingCell(resize=p.args['resize_to'],
|
|
||||||
# resolution=p.args['odm_texturing_textureResolution'],
|
|
||||||
'georeferencing': ODMGeoreferencingCell(img_size=p.args.resize_to,
|
'georeferencing': ODMGeoreferencingCell(img_size=p.args.resize_to,
|
||||||
gcp_file=p.args.odm_georeferencing_gcpFile,
|
gcp_file=p.args.gcp,
|
||||||
use_gcp=p.args.odm_georeferencing_useGcp,
|
use_exif=p.args.use_exif,
|
||||||
verbose=p.args.verbose),
|
verbose=p.args.verbose),
|
||||||
'orthophoto': ODMOrthoPhotoCell(resolution=p.args.odm_orthophoto_resolution,
|
'orthophoto': ODMOrthoPhotoCell(resolution=p.args.orthophoto_resolution,
|
||||||
verbose=p.args.verbose)
|
verbose=p.args.verbose)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cells
|
return cells
|
||||||
|
|
||||||
def configure(self, p, _i, _o):
|
def configure(self, p, _i, _o):
|
||||||
tree = types.ODM_Tree(p.args.project_path)
|
tree = types.ODM_Tree(p.args.project_path, p.args.images)
|
||||||
self.tree = ecto.Constant(value=tree)
|
self.tree = ecto.Constant(value=tree)
|
||||||
|
|
||||||
# TODO(dakota) put this somewhere better maybe
|
# TODO(dakota) put this somewhere better maybe
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import ecto
|
import ecto
|
||||||
import csv
|
import csv
|
||||||
|
import os
|
||||||
|
|
||||||
from opendm import io
|
from opendm import io
|
||||||
from opendm import log
|
from opendm import log
|
||||||
|
@ -14,8 +15,8 @@ class ODMGeoreferencingCell(ecto.Cell):
|
||||||
'points used for georeferencing.The file needs to '
|
'points used for georeferencing.The file needs to '
|
||||||
'be on the following line format: \neasting '
|
'be on the following line format: \neasting '
|
||||||
'northing height pixelrow pixelcol imagename', 'gcp_list.txt')
|
'northing height pixelrow pixelcol imagename', 'gcp_list.txt')
|
||||||
params.declare("use_gcp", 'set to true for enabling GCPs from the file above', False)
|
|
||||||
params.declare("img_size", 'image size used in calibration', 2400)
|
params.declare("img_size", 'image size used in calibration', 2400)
|
||||||
|
params.declare("use_exif", 'use exif', False)
|
||||||
params.declare("verbose", 'print additional messages to console', False)
|
params.declare("verbose", 'print additional messages to console', False)
|
||||||
|
|
||||||
def declare_io(self, params, inputs, outputs):
|
def declare_io(self, params, inputs, outputs):
|
||||||
|
@ -26,6 +27,12 @@ class ODMGeoreferencingCell(ecto.Cell):
|
||||||
outputs.declare("reconstruction", "list of ODMReconstructions", [])
|
outputs.declare("reconstruction", "list of ODMReconstructions", [])
|
||||||
|
|
||||||
def process(self, inputs, outputs):
|
def process(self, inputs, outputs):
|
||||||
|
|
||||||
|
# find a file in the root directory
|
||||||
|
def find(file, dir):
|
||||||
|
for root, dirs, files in os.walk(dir):
|
||||||
|
return '/'.join((root, file)) if file in files else None
|
||||||
|
|
||||||
# Benchmarking
|
# Benchmarking
|
||||||
start_time = system.now_raw()
|
start_time = system.now_raw()
|
||||||
|
|
||||||
|
@ -34,7 +41,9 @@ class ODMGeoreferencingCell(ecto.Cell):
|
||||||
# get inputs
|
# get inputs
|
||||||
args = self.inputs.args
|
args = self.inputs.args
|
||||||
tree = self.inputs.tree
|
tree = self.inputs.tree
|
||||||
gcpfile = io.join_paths(tree.root_path, self.params.gcp_file)
|
gcpfile = io.join_paths(tree.root_path, self.params.gcp_file) \
|
||||||
|
if self.params.gcp_file else find('gcp_list.txt', tree.root_path)
|
||||||
|
geocreated = True
|
||||||
verbose = '-verbose' if self.params.verbose else ''
|
verbose = '-verbose' if self.params.verbose else ''
|
||||||
|
|
||||||
# define paths and create working directories
|
# define paths and create working directories
|
||||||
|
@ -42,13 +51,14 @@ class ODMGeoreferencingCell(ecto.Cell):
|
||||||
|
|
||||||
# in case a gcp file it's not provided, let's try to generate it using
|
# in case a gcp file it's not provided, let's try to generate it using
|
||||||
# images metadata. Internally calls jhead.
|
# images metadata. Internally calls jhead.
|
||||||
if not self.params.use_gcp and \
|
log.ODM_DEBUG(self.params.gcp_file)
|
||||||
not io.file_exists(tree.odm_georeferencing_coords):
|
if not self.params.gcp_file: # and \
|
||||||
|
# not io.file_exists(tree.odm_georeferencing_coords):
|
||||||
|
|
||||||
log.ODM_WARNING('Warning: No coordinates file. '
|
log.ODM_WARNING('No coordinates file. '
|
||||||
'Generating coordinates file in: %s'
|
'Generating coordinates file: %s'
|
||||||
% tree.odm_georeferencing_coords)
|
% tree.odm_georeferencing_coords)
|
||||||
try:
|
|
||||||
# odm_georeference definitions
|
# odm_georeference definitions
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'bin': context.odm_modules_path,
|
'bin': context.odm_modules_path,
|
||||||
|
@ -60,16 +70,15 @@ class ODMGeoreferencingCell(ecto.Cell):
|
||||||
}
|
}
|
||||||
|
|
||||||
# run UTM extraction binary
|
# run UTM extraction binary
|
||||||
system.run('{bin}/odm_extract_utm -imagesPath {imgs}/ '
|
extract_utm = system.run_and_return('{bin}/odm_extract_utm -imagesPath {imgs}/ '
|
||||||
'-imageListFile {imgs_list} -outputCoordFile {coords} {verbose} '
|
'-imageListFile {imgs_list} -outputCoordFile {coords} {verbose} '
|
||||||
'-logFile {log}'.format(**kwargs))
|
'-logFile {log}'.format(**kwargs))
|
||||||
|
|
||||||
except Exception, e:
|
if extract_utm != '':
|
||||||
log.ODM_ERROR('Could not generate GCP file from images metadata.'
|
log.ODM_WARNING('Could not generate coordinates file. '
|
||||||
'Consider rerunning with argument --odm_georeferencing-useGcp'
|
'Ignore if there is a GCP file. Error: %s'
|
||||||
' and provide a proper GCP file')
|
% extract_utm)
|
||||||
log.ODM_ERROR(e)
|
|
||||||
return ecto.QUIT
|
|
||||||
|
|
||||||
# check if we rerun cell or not
|
# check if we rerun cell or not
|
||||||
rerun_cell = (args.rerun is not None and
|
rerun_cell = (args.rerun is not None and
|
||||||
|
@ -103,20 +112,32 @@ class ODMGeoreferencingCell(ecto.Cell):
|
||||||
else:
|
else:
|
||||||
kwargs['pc'] = tree.pmvs_model
|
kwargs['pc'] = tree.pmvs_model
|
||||||
|
|
||||||
if self.params.use_gcp and \
|
# Check to see if the GCP file exists
|
||||||
io.file_exists(gcpfile):
|
|
||||||
|
|
||||||
|
if not self.params.use_exif and (self.params.gcp_file or find('gcp_list.txt', tree.root_path)):
|
||||||
|
log.ODM_INFO('Found %s' % gcpfile)
|
||||||
|
try:
|
||||||
system.run('{bin}/odm_georef -bundleFile {bundle} -imagesPath {imgs} -imagesListPath {imgs_list} '
|
system.run('{bin}/odm_georef -bundleFile {bundle} -imagesPath {imgs} -imagesListPath {imgs_list} '
|
||||||
'-bundleResizedTo {size} -inputFile {model} -outputFile {model_geo} '
|
'-bundleResizedTo {size} -inputFile {model} -outputFile {model_geo} '
|
||||||
'-inputPointCloudFile {pc} -outputPointCloudFile {pc_geo} {verbose} '
|
'-inputPointCloudFile {pc} -outputPointCloudFile {pc_geo} {verbose} '
|
||||||
'-logFile {log} -georefFileOutputPath {geo_sys} -gcpFile {gcp} '
|
'-logFile {log} -georefFileOutputPath {geo_sys} -gcpFile {gcp} '
|
||||||
'-outputCoordFile {coords}'.format(**kwargs))
|
'-outputCoordFile {coords}'.format(**kwargs))
|
||||||
else:
|
except Exception:
|
||||||
|
log.ODM_EXCEPTION('Georeferencing failed. ')
|
||||||
|
return ecto.QUIT
|
||||||
|
elif io.file_exists(tree.odm_georeferencing_coords):
|
||||||
|
log.ODM_INFO('Running georeferencing with generated coords file.')
|
||||||
system.run('{bin}/odm_georef -bundleFile {bundle} -inputCoordFile {coords} '
|
system.run('{bin}/odm_georef -bundleFile {bundle} -inputCoordFile {coords} '
|
||||||
'-inputFile {model} -outputFile {model_geo} '
|
'-inputFile {model} -outputFile {model_geo} '
|
||||||
'-inputPointCloudFile {pc} -outputPointCloudFile {pc_geo} {verbose} '
|
'-inputPointCloudFile {pc} -outputPointCloudFile {pc_geo} {verbose} '
|
||||||
'-logFile {log} -georefFileOutputPath {geo_sys}'.format(**kwargs))
|
'-logFile {log} -georefFileOutputPath {geo_sys}'.format(**kwargs))
|
||||||
|
else:
|
||||||
|
log.ODM_WARNING('Georeferencing failed. Make sure your '
|
||||||
|
'photos have geotags in the EXIF or you have '
|
||||||
|
'provided a GCP file. ')
|
||||||
|
geocreated = False # skip the rest of the georeferencing
|
||||||
|
|
||||||
|
if geocreated:
|
||||||
# update images metadata
|
# update images metadata
|
||||||
geo_ref = types.ODM_GeoRef()
|
geo_ref = types.ODM_GeoRef()
|
||||||
geo_ref.parse_coordinate_system(tree.odm_georeferencing_coords)
|
geo_ref.parse_coordinate_system(tree.odm_georeferencing_coords)
|
||||||
|
|
|
@ -22,7 +22,7 @@ class ODMOrthoPhotoCell(ecto.Cell):
|
||||||
# Benchmarking
|
# Benchmarking
|
||||||
start_time = system.now_raw()
|
start_time = system.now_raw()
|
||||||
|
|
||||||
log.ODM_INFO('Running OMD OrthoPhoto Cell')
|
log.ODM_INFO('Running ODM Orthophoto Cell')
|
||||||
|
|
||||||
# get inputs
|
# get inputs
|
||||||
args = self.inputs.args
|
args = self.inputs.args
|
||||||
|
@ -44,7 +44,6 @@ class ODMOrthoPhotoCell(ecto.Cell):
|
||||||
# odm_orthophoto definitions
|
# odm_orthophoto definitions
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'bin': context.odm_modules_path,
|
'bin': context.odm_modules_path,
|
||||||
'model_geo': tree.odm_georeferencing_model_obj_geo,
|
|
||||||
'log': tree.odm_orthophoto_log,
|
'log': tree.odm_orthophoto_log,
|
||||||
'ortho': tree.odm_orthophoto_file,
|
'ortho': tree.odm_orthophoto_file,
|
||||||
'corners': tree.odm_orthophoto_corners,
|
'corners': tree.odm_orthophoto_corners,
|
||||||
|
@ -52,11 +51,20 @@ class ODMOrthoPhotoCell(ecto.Cell):
|
||||||
'verbose': verbose
|
'verbose': verbose
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kwargs['model_geo'] = tree.odm_georeferencing_model_obj_geo \
|
||||||
|
if io.file_exists(tree.odm_georeferencing_coords) \
|
||||||
|
else tree.odm_textured_model_obj
|
||||||
|
|
||||||
|
|
||||||
# run odm_orthophoto
|
# run odm_orthophoto
|
||||||
system.run('{bin}/odm_orthophoto -inputFile {model_geo} '
|
system.run('{bin}/odm_orthophoto -inputFile {model_geo} '
|
||||||
'-logFile {log} -outputFile {ortho} -resolution {res} {verbose} '
|
'-logFile {log} -outputFile {ortho} -resolution {res} {verbose} '
|
||||||
'-outputCornerFile {corners}'.format(**kwargs))
|
'-outputCornerFile {corners}'.format(**kwargs))
|
||||||
|
|
||||||
|
if not io.file_exists(tree.odm_georeferencing_coords):
|
||||||
|
log.ODM_WARNING('No coordinates file. A georeferenced raster '
|
||||||
|
'will not be created')
|
||||||
|
else:
|
||||||
# Create georeferenced GeoTiff
|
# Create georeferenced GeoTiff
|
||||||
geotiffcreated = False
|
geotiffcreated = False
|
||||||
georef = types.ODM_GeoRef()
|
georef = types.ODM_GeoRef()
|
||||||
|
|
Ładowanie…
Reference in New Issue