kopia lustrzana https://github.com/OpenDroneMap/ODM
Add --3d-tiles
rodzic
beb4a7e3ff
commit
ecb82b9dd5
|
@ -142,6 +142,7 @@ set(custom_libs OpenSfM
|
||||||
OpenMVS
|
OpenMVS
|
||||||
FPCFilter
|
FPCFilter
|
||||||
PyPopsift
|
PyPopsift
|
||||||
|
Obj2Tiles
|
||||||
)
|
)
|
||||||
|
|
||||||
# Build entwine only on Linux
|
# Build entwine only on Linux
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
2.8.4
|
2.8.5
|
||||||
|
|
|
@ -156,6 +156,12 @@ def config(argv=None, parser=None):
|
||||||
'Can be one of: %(choices)s. Default: '
|
'Can be one of: %(choices)s. Default: '
|
||||||
'%(default)s'))
|
'%(default)s'))
|
||||||
|
|
||||||
|
parser.add_argument('--3d-tiles',
|
||||||
|
action=StoreTrue,
|
||||||
|
nargs=0,
|
||||||
|
default=False,
|
||||||
|
help='Generate OGC 3D Tiles outputs. Default: %(default)s')
|
||||||
|
|
||||||
parser.add_argument('--matcher-neighbors',
|
parser.add_argument('--matcher-neighbors',
|
||||||
metavar='<positive integer>',
|
metavar='<positive integer>',
|
||||||
action=StoreValue,
|
action=StoreValue,
|
||||||
|
@ -773,9 +779,6 @@ def config(argv=None, parser=None):
|
||||||
if args.fast_orthophoto:
|
if args.fast_orthophoto:
|
||||||
log.ODM_INFO('Fast orthophoto is turned on, automatically setting --skip-3dmodel')
|
log.ODM_INFO('Fast orthophoto is turned on, automatically setting --skip-3dmodel')
|
||||||
args.skip_3dmodel = True
|
args.skip_3dmodel = True
|
||||||
# if not 'sfm_algorithm_is_set' in args:
|
|
||||||
# log.ODM_INFO('Fast orthophoto is turned on, automatically setting --sfm-algorithm to triangulation')
|
|
||||||
# args.sfm_algorithm = 'triangulation'
|
|
||||||
|
|
||||||
if args.pc_rectify and not args.pc_classify:
|
if args.pc_rectify and not args.pc_classify:
|
||||||
log.ODM_INFO("Ground rectify is turned on, automatically turning on point cloud classification")
|
log.ODM_INFO("Ground rectify is turned on, automatically turning on point cloud classification")
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import shutil
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
from opendm.utils import double_quote
|
||||||
|
from opendm import io
|
||||||
|
from opendm import log
|
||||||
|
from opendm import system
|
||||||
|
from opendm import concurrency
|
||||||
|
import fiona
|
||||||
|
from shapely.geometry import shape
|
||||||
|
|
||||||
|
def build_textured_model(input_obj, output_path, reference_lla = None, model_bounds_file=None, rerun=False):
|
||||||
|
if not os.path.isfile(input_obj):
|
||||||
|
log.ODM_WARNING("No input OBJ file to process")
|
||||||
|
return
|
||||||
|
|
||||||
|
if rerun and io.dir_exists(output_path):
|
||||||
|
log.ODM_WARNING("Removing previous 3D tiles directory: %s" % output_path)
|
||||||
|
shutil.rmtree(output_path)
|
||||||
|
|
||||||
|
log.ODM_INFO("Generating OGC 3D Tiles textured model")
|
||||||
|
lat = lon = alt = 0
|
||||||
|
|
||||||
|
# Read reference_lla.json (if provided)
|
||||||
|
if reference_lla is not None and os.path.isfile(reference_lla):
|
||||||
|
try:
|
||||||
|
with open(reference_lla) as f:
|
||||||
|
reference_lla = json.loads(f.read())
|
||||||
|
lat = reference_lla['latitude']
|
||||||
|
lon = reference_lla['longitude']
|
||||||
|
alt = reference_lla['altitude']
|
||||||
|
except Exception as e:
|
||||||
|
log.ODM_WARNING("Cannot read %s: %s" % (reference_lla, str(e)))
|
||||||
|
|
||||||
|
# Read model bounds (if provided)
|
||||||
|
divisions = 1 # default
|
||||||
|
DIV_THRESHOLD = 10000 # m^2 (this is somewhat arbitrary)
|
||||||
|
|
||||||
|
if model_bounds_file is not None and os.path.isfile(model_bounds_file):
|
||||||
|
try:
|
||||||
|
with fiona.open(model_bounds_file, 'r') as f:
|
||||||
|
if len(f) == 1:
|
||||||
|
poly = shape(f[1]['geometry'])
|
||||||
|
area = poly.area
|
||||||
|
log.ODM_INFO("Approximate area: %s m^2" % round(area, 2))
|
||||||
|
|
||||||
|
if area < DIV_THRESHOLD:
|
||||||
|
divisions = 0
|
||||||
|
else:
|
||||||
|
divisions = math.ceil(math.log((area / DIV_THRESHOLD), 4))
|
||||||
|
else:
|
||||||
|
log.ODM_WARNING("Invalid boundary file: %s" % model_bounds_file)
|
||||||
|
except Exception as e:
|
||||||
|
log.ODM_WARNING("Cannot read %s: %s" % (model_bounds_file, str(e)))
|
||||||
|
|
||||||
|
try:
|
||||||
|
kwargs = {
|
||||||
|
'input': input_obj,
|
||||||
|
'output': output_path,
|
||||||
|
'divisions': divisions,
|
||||||
|
'lat': lat,
|
||||||
|
'lon': lon,
|
||||||
|
'alt': alt,
|
||||||
|
}
|
||||||
|
system.run('Obj2Tiles --input "{input}" --output "{output}" --divisions {divisions} '.format(**kwargs))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log.ODM_WARNING("Cannot build 3D tiles textured model: %s" % str(e))
|
||||||
|
|
||||||
|
|
||||||
|
def build_3dtiles(args, tree, reconstruction, rerun=False):
|
||||||
|
tiles_output_path = tree.ogc_tiles
|
||||||
|
model_output_path = os.path.join(tiles_output_path, "textured_model")
|
||||||
|
|
||||||
|
if rerun and os.path.exists(tiles_output_path):
|
||||||
|
shutil.rmtree(tiles_output_path)
|
||||||
|
|
||||||
|
if not os.path.isdir(tiles_output_path):
|
||||||
|
os.mkdir(tiles_output_path)
|
||||||
|
|
||||||
|
if not os.path.isdir(model_output_path) or rerun:
|
||||||
|
reference_lla = os.path.join(tree.opensfm, "reference_lla.json")
|
||||||
|
model_bounds_file = os.path.join(tree.odm_georeferencing, 'odm_georeferenced_model.bounds.gpkg')
|
||||||
|
|
||||||
|
input_obj = os.path.join(tree.odm_texturing, tree.odm_textured_model_obj)
|
||||||
|
if not os.path.isfile(input_obj):
|
||||||
|
input_obj = os.path.join(tree.odm_25dtexturing, tree.odm_textured_model_obj)
|
||||||
|
|
||||||
|
build_textured_model(input_obj, model_output_path, reference_lla, model_bounds_file, rerun)
|
||||||
|
else:
|
||||||
|
log.ODM_WARNING("OGC 3D Tiles model %s already generated" % model_output_path)
|
|
@ -289,6 +289,7 @@ class ODM_Tree(object):
|
||||||
|
|
||||||
# Tiles
|
# Tiles
|
||||||
self.entwine_pointcloud = self.path("entwine_pointcloud")
|
self.entwine_pointcloud = self.path("entwine_pointcloud")
|
||||||
|
self.ogc_tiles = self.path("3d_tiles")
|
||||||
|
|
||||||
def path(self, *args):
|
def path(self, *args):
|
||||||
return os.path.join(self.root_path, *args)
|
return os.path.join(self.root_path, *args)
|
||||||
|
|
|
@ -64,6 +64,7 @@ def get_processing_results_paths():
|
||||||
"dsm_tiles",
|
"dsm_tiles",
|
||||||
"dtm_tiles",
|
"dtm_tiles",
|
||||||
"orthophoto_tiles",
|
"orthophoto_tiles",
|
||||||
|
"3d_tiles",
|
||||||
"images.json",
|
"images.json",
|
||||||
"cameras.json",
|
"cameras.json",
|
||||||
"log.json",
|
"log.json",
|
||||||
|
|
|
@ -5,6 +5,7 @@ from opendm import io
|
||||||
from opendm import log
|
from opendm import log
|
||||||
from opendm import types
|
from opendm import types
|
||||||
from opendm.utils import copy_paths, get_processing_results_paths
|
from opendm.utils import copy_paths, get_processing_results_paths
|
||||||
|
from opendm.ogctiles import build_3dtiles
|
||||||
|
|
||||||
class ODMPostProcess(types.ODM_Stage):
|
class ODMPostProcess(types.ODM_Stage):
|
||||||
def process(self, args, outputs):
|
def process(self, args, outputs):
|
||||||
|
@ -43,7 +44,10 @@ class ODMPostProcess(types.ODM_Stage):
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
log.ODM_WARNING("Cannot open %s for writing, skipping GCP embedding" % product)
|
log.ODM_WARNING("Cannot open %s for writing, skipping GCP embedding" % product)
|
||||||
|
|
||||||
|
if getattr(args, '3d_tiles'):
|
||||||
|
build_3dtiles(args, tree, reconstruction, self.rerun())
|
||||||
|
|
||||||
if args.copy_to:
|
if args.copy_to:
|
||||||
try:
|
try:
|
||||||
copy_paths([os.path.join(args.project_path, p) for p in get_processing_results_paths()], args.copy_to, self.rerun())
|
copy_paths([os.path.join(args.project_path, p) for p in get_processing_results_paths()], args.copy_to, self.rerun())
|
||||||
|
|
Ładowanie…
Reference in New Issue