OpenDroneMap-ODM/stages/mvstex.py

185 wiersze
8.1 KiB
Python

import os, shutil
from opendm import log
from opendm import io
from opendm import system
from opendm import context
from opendm import types
from opendm.multispectral import get_primary_band_name
from opendm.photo import find_largest_photo_dim
from opendm.objpacker import obj_pack
from opendm.gltf import obj2glb
class ODMMvsTexStage(types.ODM_Stage):
def process(self, args, outputs):
tree = outputs['tree']
reconstruction = outputs['reconstruction']
max_dim = find_largest_photo_dim(reconstruction.photos)
max_texture_size = 8 * 1024 # default
if max_dim > 8000:
log.ODM_INFO("Large input images (%s pixels), increasing maximum texture size." % max_dim)
max_texture_size *= 3
class nonloc:
runs = []
def add_run(nvm_file, primary=True, band=None):
subdir = ""
if not primary and band is not None:
subdir = band
if not args.skip_3dmodel and (primary or args.use_3dmesh):
nonloc.runs += [{
'out_dir': os.path.join(tree.odm_texturing, subdir),
'model': tree.odm_mesh,
'nadir': False,
'primary': primary,
'nvm_file': nvm_file,
'labeling_file': os.path.join(tree.odm_texturing, "odm_textured_model_geo_labeling.vec") if subdir else None
}]
if not args.use_3dmesh:
nonloc.runs += [{
'out_dir': os.path.join(tree.odm_25dtexturing, subdir),
'model': tree.odm_25dmesh,
'nadir': True,
'primary': primary,
'nvm_file': nvm_file,
'labeling_file': os.path.join(tree.odm_25dtexturing, "odm_textured_model_geo_labeling.vec") if subdir else None
}]
if reconstruction.multi_camera:
for band in reconstruction.multi_camera:
primary = band['name'] == get_primary_band_name(reconstruction.multi_camera, args.primary_band)
nvm_file = os.path.join(tree.opensfm, "undistorted", "reconstruction_%s.nvm" % band['name'].lower())
add_run(nvm_file, primary, band['name'].lower())
# Sort to make sure primary band is processed first
nonloc.runs.sort(key=lambda r: r['primary'], reverse=True)
else:
add_run(tree.opensfm_reconstruction_nvm)
progress_per_run = 100.0 / len(nonloc.runs)
progress = 0.0
for r in nonloc.runs:
if not io.dir_exists(r['out_dir']):
system.mkdir_p(r['out_dir'])
odm_textured_model_obj = os.path.join(r['out_dir'], tree.odm_textured_model_obj)
unaligned_obj = io.related_file_path(odm_textured_model_obj, postfix="_unaligned")
if not io.file_exists(odm_textured_model_obj) or self.rerun():
log.ODM_INFO('Writing MVS Textured file in: %s'
% odm_textured_model_obj)
if os.path.isfile(unaligned_obj):
os.unlink(unaligned_obj)
# Format arguments to fit Mvs-Texturing app
skipGlobalSeamLeveling = ""
keepUnseenFaces = ""
nadir = ""
if args.texturing_skip_global_seam_leveling:
skipGlobalSeamLeveling = "--skip_global_seam_leveling"
if args.texturing_keep_unseen_faces:
keepUnseenFaces = "--keep_unseen_faces"
if (r['nadir']):
nadir = '--nadir_mode'
# mvstex definitions
kwargs = {
'bin': context.mvstex_path,
'out_dir': os.path.join(r['out_dir'], "odm_textured_model_geo"),
'model': r['model'],
'dataTerm': 'gmi',
'outlierRemovalType': 'gauss_clamping',
'skipGlobalSeamLeveling': skipGlobalSeamLeveling,
'keepUnseenFaces': keepUnseenFaces,
'toneMapping': 'none',
'nadirMode': nadir,
'maxTextureSize': '--max_texture_size=%s' % max_texture_size,
'nvm_file': r['nvm_file'],
'intermediate': '--no_intermediate_results' if (r['labeling_file'] or not reconstruction.multi_camera) else '',
'labelingFile': '-L "%s"' % r['labeling_file'] if r['labeling_file'] else ''
}
mvs_tmp_dir = os.path.join(r['out_dir'], 'tmp')
# Make sure tmp directory is empty
if io.dir_exists(mvs_tmp_dir):
log.ODM_INFO("Removing old tmp directory {}".format(mvs_tmp_dir))
shutil.rmtree(mvs_tmp_dir)
# run texturing binary
system.run('"{bin}" "{nvm_file}" "{model}" "{out_dir}" '
'-d {dataTerm} -o {outlierRemovalType} '
'-t {toneMapping} '
'{intermediate} '
'{skipGlobalSeamLeveling} '
'{keepUnseenFaces} '
'{nadirMode} '
'{labelingFile} '
'{maxTextureSize} '.format(**kwargs))
if r['primary'] and (not r['nadir'] or args.skip_3dmodel):
# GlTF?
if args.gltf:
log.ODM_INFO("Generating glTF Binary")
odm_textured_model_glb = os.path.join(r['out_dir'], tree.odm_textured_model_glb)
try:
obj2glb(odm_textured_model_obj, odm_textured_model_glb, rtc=reconstruction.get_proj_offset(), _info=log.ODM_INFO)
except Exception as e:
log.ODM_WARNING(str(e))
# Single material?
if args.texturing_single_material:
log.ODM_INFO("Packing to single material")
packed_dir = os.path.join(r['out_dir'], 'packed')
if io.dir_exists(packed_dir):
log.ODM_INFO("Removing old packed directory {}".format(packed_dir))
shutil.rmtree(packed_dir)
try:
obj_pack(os.path.join(r['out_dir'], tree.odm_textured_model_obj), packed_dir, _info=log.ODM_INFO)
# Move packed/* into texturing folder
system.delete_files(r['out_dir'], (".vec", ))
system.move_files(packed_dir, r['out_dir'])
if os.path.isdir(packed_dir):
os.rmdir(packed_dir)
except Exception as e:
log.ODM_WARNING(str(e))
# Backward compatibility: copy odm_textured_model_geo.mtl to odm_textured_model.mtl
# for certain older WebODM clients which expect a odm_textured_model.mtl
# to be present for visualization
# We should remove this at some point in the future
geo_mtl = os.path.join(r['out_dir'], 'odm_textured_model_geo.mtl')
if io.file_exists(geo_mtl):
nongeo_mtl = os.path.join(r['out_dir'], 'odm_textured_model.mtl')
shutil.copy(geo_mtl, nongeo_mtl)
progress += progress_per_run
self.update_progress(progress)
else:
log.ODM_WARNING('Found a valid ODM Texture file in: %s'
% odm_textured_model_obj)
if args.optimize_disk_space:
for r in nonloc.runs:
if io.file_exists(r['model']):
os.remove(r['model'])
undistorted_images_path = os.path.join(tree.opensfm, "undistorted", "images")
if io.dir_exists(undistorted_images_path):
shutil.rmtree(undistorted_images_path)