kopia lustrzana https://github.com/OpenDroneMap/ODM
Merge branch 'master' into image-load-process
commit
fc2ccf4ce5
|
@ -63,7 +63,6 @@ Include details about your configuration and environment:
|
|||
* Problem happens with all datasets and projects, not only some datasets or projects: [Yes/No]
|
||||
|
||||
### Pull Requests
|
||||
* ***Pull Requests should be made to the `dev` branch unless it is a critical bug.***
|
||||
* Include screenshots and animated GIFs in your pull request whenever possible.
|
||||
* Follow the [PEP8 Python Style Guide](https://www.python.org/dev/peps/pep-0008/).
|
||||
* End files with a newline.
|
||||
|
|
54
README.md
54
README.md
|
@ -29,17 +29,22 @@ Requires Ubuntu 14.04 or later, see https://github.com/OpenDroneMap/odm_vagrant
|
|||
|
||||
**[Download the latest release here](https://github.com/OpenDroneMap/OpenDroneMap/releases)**
|
||||
|
||||
Current version: 0.2 (beta)
|
||||
Current version: 0.2 (this software is in beta)
|
||||
|
||||
1. Extract and enter the OpenDroneMap directory
|
||||
2. Run `bash configure.sh`
|
||||
3. Download and extract a sample dataset [here](https://github.com/OpenDroneMap/odm_data_aukerman/archive/master.zip)
|
||||
4. Run `./run.sh --project-path <PATH>`, replacing `<PATH>` with the dataset path.
|
||||
5. Enter dataset directory to view results:
|
||||
- orthophoto: odm_orthophoto/odm_orthophoto.tif
|
||||
- textured mesh model: odm_texturing/odm_textured_model_geo.obj
|
||||
- point cloud (georeferenced): odm_georeferencing/odm_georeferenced_model.ply
|
||||
|
||||
See below for more detailed installation instructions.
|
||||
|
||||
### Installation
|
||||
|
||||
Before installing you need to set your environment variables. Open the ~/.bashrc file on your machine and add the following 3 lines at the end. The file can be opened with ```gedit ~/.bashrc``` if you are using an Ubuntu desktop environment. Be sure to replace the "/your/path/" with the correct path to the location where you extracted OpenDroneMap:
|
||||
|
||||
export PYTHONPATH=$PYTHONPATH:/your/path/OpenDroneMap/SuperBuild/install/lib/python2.7/dist-packages
|
||||
export PYTHONPATH=$PYTHONPATH:/your/path/OpenDroneMap/SuperBuild/src/opensfm
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/your/path/OpenDroneMap/SuperBuild/install/lib
|
||||
|
||||
Now, enter the OpenDroneMap directory and compile all of the code by executing a single configuration script:
|
||||
Extract and enter the downloaded OpenDroneMap directory and compile all of the code by executing a single configuration script:
|
||||
|
||||
bash configure.sh
|
||||
|
||||
|
@ -48,6 +53,16 @@ For Ubuntu 15.10 users, this will help you get running:
|
|||
sudo apt-get install python-xmltodict
|
||||
sudo ln -s /usr/lib/x86_64-linux-gnu/libproj.so.9 /usr/lib/libproj.so
|
||||
|
||||
### Environment Variables
|
||||
|
||||
There are some environmental variables that need to be set. Open the ~/.bashrc file on your machine and add the following 3 lines at the end. The file can be opened with ```gedit ~/.bashrc``` if you are using an Ubuntu desktop environment. Be sure to replace the "/your/path/" with the correct path to the location where you extracted OpenDroneMap:
|
||||
|
||||
export PYTHONPATH=$PYTHONPATH:/your/path/OpenDroneMap/SuperBuild/install/lib/python2.7/dist-packages
|
||||
export PYTHONPATH=$PYTHONPATH:/your/path/OpenDroneMap/SuperBuild/src/opensfm
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/your/path/OpenDroneMap/SuperBuild/install/lib
|
||||
|
||||
Note that using `run.sh` sets these temporarily in the shell.
|
||||
|
||||
### Run OpenDroneMap
|
||||
|
||||
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
|
||||
|
@ -109,11 +124,9 @@ 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
|
||||
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
|
||||
instructions up until "Create a Docker group" inclusive. Once Docker is installed, an OpenDroneMap Docker image can be created
|
||||
instructions through "Create a Docker group". Once Docker is installed, an OpenDroneMap Docker image can be created
|
||||
like so:
|
||||
|
||||
git clone https://github.com/OpenDroneMap/OpenDroneMap.git
|
||||
cd OpenDroneMap
|
||||
docker build -t packages -f packages.Dockerfile .
|
||||
docker build -t odm_image .
|
||||
docker run -it --user root\
|
||||
|
@ -122,8 +135,8 @@ like so:
|
|||
-v /path/to/gcp_list.txt:/code/gcp_list.txt \
|
||||
--rm odm_image --project-path /project
|
||||
|
||||
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.
|
||||
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 pass in custom parameters to the run.py script, simply pass it as arguments to the `docker run` command.
|
||||
|
||||
|
@ -133,22 +146,11 @@ A web interface and API to OpenDroneMap is currently under active development in
|
|||
|
||||
## Examples
|
||||
|
||||
Here are some other videos, which may be outdated:
|
||||
|
||||
- https://www.youtube.com/watch?v=7ZTufQkODLs (2015-01-30)
|
||||
- https://www.youtube.com/watch?v=m0i4GQdfl8A (2015-03-15)
|
||||
|
||||
Now that texturing is in the code base, you can access the full textured meshes using MeshLab.
|
||||
Open MeshLab, choose `File:Import Mesh` and choose your textured mesh from a location similar to the following:
|
||||
`reconstruction-with-image-size-1200-results\odm_texturing\odm_textured_model.obj`. Long term, the aim is for
|
||||
the toolchain to also be able to optionally push to a variety of online data repositories, pushing hi-resolution
|
||||
aerials to [OpenAerialMap](https://openaerialmap.org/), point clouds to [OpenTopography](http://opentopography.org/),
|
||||
and pushing digital elevation models to an emerging global repository (yet to be named...). That leaves only
|
||||
digital surface model meshes and UV textured meshes with no global repository home.
|
||||
Coming soon...
|
||||
|
||||
## Documentation:
|
||||
|
||||
For documentation, please take a look at our [wiki](https://github.com/OpenDroneMap/OpenDroneMap/wiki).Check here first if you are heaving problems. If you still need help, look through the issue queue or create one. There's also a general help chat [here](https://gitter.im/OpenDroneMap/generalhelp).
|
||||
For documentation, please take a look at our [wiki](https://github.com/OpenDroneMap/OpenDroneMap/wiki).Check here first if you are having problems. If you still need help, look through the issue queue or create one. There's also a general help chat [here](https://gitter.im/OpenDroneMap/generalhelp).
|
||||
|
||||
## Developers
|
||||
|
||||
|
|
|
@ -1197,7 +1197,7 @@ void Georef::chooseBestCameraTriplet(size_t &cam0, size_t &cam1, size_t &cam2)
|
|||
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 curren camera triplet.
|
||||
// The total error for the current camera triplet.
|
||||
double totError = 0.0;
|
||||
|
||||
for(size_t r = 0; r < cameras_.size(); ++r)
|
||||
|
|
|
@ -279,6 +279,12 @@ def config():
|
|||
default=False,
|
||||
help='compress the results using gunzip')
|
||||
|
||||
parser.add_argument('--verbose', '-v',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Print additional messages to the console\n'
|
||||
'Default: %(default)s')
|
||||
|
||||
parser.add_argument('--time',
|
||||
action='store_true',
|
||||
default=False,
|
||||
|
|
|
@ -18,22 +18,8 @@ logging.basicConfig(level=logging.DEBUG,
|
|||
format='%(levelname)-14s %(message)s' + ENDC)
|
||||
|
||||
|
||||
def ODM_INFO(str):
|
||||
logging.info(str)
|
||||
|
||||
|
||||
def ODM_WARNING(str):
|
||||
logging.warning(str)
|
||||
|
||||
|
||||
def ODM_ERROR(str):
|
||||
logging.error(str)
|
||||
|
||||
|
||||
def ODM_EXCEPTION(str):
|
||||
logging.exception(str)
|
||||
|
||||
|
||||
def ODM_DEBUG(str):
|
||||
logging.debug(str)
|
||||
|
||||
ODM_INFO = logging.info
|
||||
ODM_WARNING = logging.warning
|
||||
ODM_ERROR = logging.error
|
||||
ODM_EXCEPTION = logging.exception
|
||||
ODM_DEBUG = logging.debug
|
||||
|
|
|
@ -16,15 +16,16 @@ def get_ccd_widths():
|
|||
sensor_data = json.loads(f.read())
|
||||
return dict(zip(map(string.lower, sensor_data.keys()), sensor_data.values()))
|
||||
|
||||
|
||||
def run(cmd):
|
||||
"""Run a system command"""
|
||||
log.ODM_DEBUG('running %s' % cmd)
|
||||
returnCode = os.system(cmd)
|
||||
retcode = subprocess.call(cmd, shell=True)
|
||||
|
||||
if (returnCode != 0):
|
||||
log.ODM_ERROR("quitting cause: \n\t" + cmd + "\nreturned with code " +
|
||||
str(returnCode) + ".\n")
|
||||
sys.exit('An error occurred. Check stdout above or the logs.')
|
||||
if retcode < 0:
|
||||
raise Exception("Child was terminated by signal {}".format(-retcode))
|
||||
elif retcode > 0:
|
||||
raise Exception("Child returned {}".format(retcode))
|
||||
|
||||
|
||||
def now():
|
||||
|
|
|
@ -56,7 +56,8 @@ class ODMApp(ecto.BlackBox):
|
|||
'meshing': ODMeshingCell(max_vertex=p.args.mesh_size,
|
||||
oct_tree=p.args.mesh_octree_depth,
|
||||
samples=p.args.mesh_samples,
|
||||
solver=p.args.mesh_solver_divide),
|
||||
solver=p.args.mesh_solver_divide,
|
||||
verbose=p.args.verbose),
|
||||
'texturing': ODMMvsTexCell(data_term=p.args.texturing_data_term,
|
||||
outlier_rem_type=p.args.texturing_outlier_removal_type,
|
||||
skip_vis_test=p.args.texturing_skip_visibility_test,
|
||||
|
@ -65,9 +66,10 @@ class ODMApp(ecto.BlackBox):
|
|||
skip_hole_fill=p.args.texturing_skip_hole_filling,
|
||||
keep_unseen_faces=p.args.texturing_keep_unseen_faces),
|
||||
'georeferencing': ODMGeoreferencingCell(img_size=p.args.resize_to,
|
||||
gcp_file=p.args.gcp),
|
||||
'orthophoto': ODMOrthoPhotoCell(resolution=p.args.orthophoto_resolution)
|
||||
|
||||
gcp_file=p.args.gcp,
|
||||
verbose=p.args.verbose),
|
||||
'orthophoto': ODMOrthoPhotoCell(resolution=p.args.orthophoto_resolution,
|
||||
verbose=p.args.verbose)
|
||||
}
|
||||
|
||||
return cells
|
||||
|
|
|
@ -16,6 +16,7 @@ class ODMGeoreferencingCell(ecto.Cell):
|
|||
'be on the following line format: \neasting '
|
||||
'northing height pixelrow pixelcol imagename', 'gcp_list.txt')
|
||||
params.declare("img_size", 'image size used in calibration', 2400)
|
||||
params.declare("verbose", 'print additional messages to console', False)
|
||||
|
||||
def declare_io(self, params, inputs, outputs):
|
||||
inputs.declare("tree", "Struct with paths", [])
|
||||
|
@ -42,6 +43,7 @@ class ODMGeoreferencingCell(ecto.Cell):
|
|||
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 ''
|
||||
|
||||
# define paths and create working directories
|
||||
system.mkdir_p(tree.odm_georeferencing)
|
||||
|
@ -63,11 +65,12 @@ class ODMGeoreferencingCell(ecto.Cell):
|
|||
'imgs_list': tree.opensfm_bundle_list,
|
||||
'coords': tree.odm_georeferencing_coords,
|
||||
'log': tree.odm_georeferencing_utm_log
|
||||
'verbose': verbose
|
||||
}
|
||||
|
||||
# run UTM extraction binary
|
||||
extract_utm = system.run_and_return('{bin}/odm_extract_utm -imagesPath {imgs}/ '
|
||||
'-imageListFile {imgs_list} -outputCoordFile {coords} '
|
||||
'-imageListFile {imgs_list} -outputCoordFile {coords} {verbose} '
|
||||
'-logFile {log}'.format(**kwargs))
|
||||
|
||||
if extract_utm != '':
|
||||
|
@ -75,6 +78,7 @@ class ODMGeoreferencingCell(ecto.Cell):
|
|||
'Ignore if there is a GCP file. Error: %s'
|
||||
% extract_utm)
|
||||
|
||||
|
||||
# check if we rerun cell or not
|
||||
rerun_cell = (args.rerun is not None and
|
||||
args.rerun == 'odm_georeferencing') or \
|
||||
|
@ -99,6 +103,7 @@ class ODMGeoreferencingCell(ecto.Cell):
|
|||
'model_geo': tree.odm_georeferencing_model_obj_geo,
|
||||
'size': self.params.img_size,
|
||||
'gcp': gcpfile,
|
||||
'verbose': verbose
|
||||
|
||||
}
|
||||
if args.use_opensfm_pointcloud:
|
||||
|
@ -112,14 +117,14 @@ class ODMGeoreferencingCell(ecto.Cell):
|
|||
log.ODM_INFO('Found %s' % gcpfile)
|
||||
system.run('{bin}/odm_georef -bundleFile {bundle} -imagesPath {imgs} -imagesListPath {imgs_list} '
|
||||
'-bundleResizedTo {size} -inputFile {model} -outputFile {model_geo} '
|
||||
'-inputPointCloudFile {pc} -outputPointCloudFile {pc_geo} '
|
||||
'-inputPointCloudFile {pc} -outputPointCloudFile {pc_geo} {verbose} '
|
||||
'-logFile {log} -georefFileOutputPath {geo_sys} -gcpFile {gcp} '
|
||||
'-outputCoordFile {coords}'.format(**kwargs))
|
||||
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} '
|
||||
'-inputFile {model} -outputFile {model_geo} '
|
||||
'-inputPointCloudFile {pc} -outputPointCloudFile {pc_geo} '
|
||||
'-inputPointCloudFile {pc} -outputPointCloudFile {pc_geo} {verbose} '
|
||||
'-logFile {log} -georefFileOutputPath {geo_sys}'.format(**kwargs))
|
||||
else:
|
||||
log.ODM_WARNING('Georeferencing failed. Make sure your '
|
||||
|
|
|
@ -19,6 +19,7 @@ class ODMeshingCell(ecto.Cell):
|
|||
'is solved in the surface reconstruction step. '
|
||||
'Increasing this value increases computation '
|
||||
'times slightly but helps reduce memory usage.', 9)
|
||||
params.declare("verbose", 'print additional messages to console', False)
|
||||
|
||||
def declare_io(self, params, inputs, outputs):
|
||||
inputs.declare("tree", "Struct with paths", [])
|
||||
|
@ -36,6 +37,7 @@ class ODMeshingCell(ecto.Cell):
|
|||
# get inputs
|
||||
args = self.inputs.args
|
||||
tree = self.inputs.tree
|
||||
verbose = '-verbose' if self.params.verbose else ''
|
||||
|
||||
# define paths and create working directories
|
||||
system.mkdir_p(tree.odm_meshing)
|
||||
|
@ -57,7 +59,8 @@ class ODMeshingCell(ecto.Cell):
|
|||
'max_vertex': self.params.max_vertex,
|
||||
'oct_tree': self.params.oct_tree,
|
||||
'samples': self.params.samples,
|
||||
'solver': self.params.solver
|
||||
'solver': self.params.solver,
|
||||
'verbose': verbose
|
||||
}
|
||||
if args.use_opensfm_pointcloud:
|
||||
kwargs['infile'] = tree.opensfm_model
|
||||
|
@ -67,7 +70,7 @@ class ODMeshingCell(ecto.Cell):
|
|||
# run meshing binary
|
||||
system.run('{bin}/odm_meshing -inputFile {infile} '
|
||||
'-outputFile {outfile} -logFile {log} '
|
||||
'-maxVertexCount {max_vertex} -octreeDepth {oct_tree} '
|
||||
'-maxVertexCount {max_vertex} -octreeDepth {oct_tree} {verbose} '
|
||||
'-samplesPerNode {samples} -solverDivide {solver}'.format(**kwargs))
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid ODM Mesh file in: %s' %
|
||||
|
|
|
@ -10,6 +10,7 @@ from opendm import types
|
|||
class ODMOrthoPhotoCell(ecto.Cell):
|
||||
def declare_params(self, params):
|
||||
params.declare("resolution", 'Orthophoto ground resolution in pixels/meter', 20)
|
||||
params.declare("verbose", 'print additional messages to console', False)
|
||||
|
||||
def declare_io(self, params, inputs, outputs):
|
||||
inputs.declare("tree", "Struct with paths", [])
|
||||
|
@ -26,6 +27,7 @@ class ODMOrthoPhotoCell(ecto.Cell):
|
|||
# get inputs
|
||||
args = self.inputs.args
|
||||
tree = self.inputs.tree
|
||||
verbose = '-verbose' if self.params.verbose else ''
|
||||
|
||||
# define paths and create working directories
|
||||
system.mkdir_p(tree.odm_orthophoto)
|
||||
|
@ -45,7 +47,8 @@ class ODMOrthoPhotoCell(ecto.Cell):
|
|||
'log': tree.odm_orthophoto_log,
|
||||
'ortho': tree.odm_orthophoto_file,
|
||||
'corners': tree.odm_orthophoto_corners,
|
||||
'res': self.params.resolution
|
||||
'res': self.params.resolution,
|
||||
'verbose': verbose
|
||||
}
|
||||
|
||||
kwargs['model_geo'] = tree.odm_georeferencing_model_obj_geo \
|
||||
|
@ -55,7 +58,7 @@ class ODMOrthoPhotoCell(ecto.Cell):
|
|||
|
||||
# run odm_orthophoto
|
||||
system.run('{bin}/odm_orthophoto -inputFile {model_geo} '
|
||||
'-logFile {log} -outputFile {ortho} -resolution {res} '
|
||||
'-logFile {log} -outputFile {ortho} -resolution {res} {verbose} '
|
||||
'-outputCornerFile {corners}'.format(**kwargs))
|
||||
|
||||
if not io.file_exists(tree.odm_georeferencing_coords):
|
||||
|
|
|
@ -13,6 +13,7 @@ class ODMTexturingCell(ecto.Cell):
|
|||
'greater than textureWithSize.', 4096)
|
||||
params.declare("size", 'The resolution to rescale the images performing '
|
||||
'the texturing.', 3600)
|
||||
params.declare("verbose", 'print additional messages to console', False)
|
||||
|
||||
def declare_io(self, params, inputs, outputs):
|
||||
inputs.declare("tree", "Struct with paths", [])
|
||||
|
@ -30,6 +31,7 @@ class ODMTexturingCell(ecto.Cell):
|
|||
# get inputs
|
||||
args = self.inputs.args
|
||||
tree = self.inputs.tree
|
||||
verbose = '-verbose' if self.params.verbose else ''
|
||||
|
||||
# define paths and create working directories
|
||||
system.mkdir_p(tree.odm_texturing)
|
||||
|
@ -56,14 +58,15 @@ class ODMTexturingCell(ecto.Cell):
|
|||
'log': tree.odm_texuring_log,
|
||||
'resize': self.params.resize,
|
||||
'resolution': self.params.resolution,
|
||||
'size': self.params.size
|
||||
'size': self.params.size,
|
||||
'verbose': verbose
|
||||
}
|
||||
|
||||
# run texturing binary
|
||||
system.run('{bin}/odm_texturing -bundleFile {bundle} '
|
||||
'-imagesPath {imgs_path} -imagesListPath {imgs_list} '
|
||||
'-inputModelPath {model} -outputFolder {out_dir}/ '
|
||||
'-textureResolution {resolution} -bundleResizedTo {resize} '
|
||||
'-textureResolution {resolution} -bundleResizedTo {resize} {verbose} '
|
||||
'-textureWithSize {size} -logFile {log}'.format(**kwargs))
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid ODM Texture file in: %s'
|
||||
|
|
Ładowanie…
Reference in New Issue