2019-03-01 17:35:53 +00:00
|
|
|
import sys
|
2019-03-06 02:45:08 +00:00
|
|
|
import os
|
2020-03-30 14:32:21 +00:00
|
|
|
import shutil
|
|
|
|
import glob
|
2015-11-26 12:15:02 +00:00
|
|
|
|
2015-11-20 10:00:43 +00:00
|
|
|
from opendm import log
|
2015-11-26 12:15:02 +00:00
|
|
|
from opendm import io
|
2015-11-20 10:00:43 +00:00
|
|
|
from opendm import system
|
|
|
|
from opendm import context
|
2018-08-11 16:45:21 +00:00
|
|
|
from opendm import gsd
|
2019-03-06 00:38:46 +00:00
|
|
|
from opendm import point_cloud
|
2019-04-22 19:14:39 +00:00
|
|
|
from opendm import types
|
2019-04-25 15:40:45 +00:00
|
|
|
from opendm.osfm import OSFMContext
|
2020-03-06 18:04:34 +00:00
|
|
|
from opendm import multispectral
|
2016-02-26 18:50:12 +00:00
|
|
|
|
2019-04-22 19:14:39 +00:00
|
|
|
class ODMOpenSfMStage(types.ODM_Stage):
|
|
|
|
def process(self, args, outputs):
|
|
|
|
tree = outputs['tree']
|
|
|
|
reconstruction = outputs['reconstruction']
|
2018-01-26 19:38:26 +00:00
|
|
|
photos = reconstruction.photos
|
2015-11-26 12:15:02 +00:00
|
|
|
|
2015-11-27 16:51:21 +00:00
|
|
|
if not photos:
|
2016-03-08 18:26:58 +00:00
|
|
|
log.ODM_ERROR('Not enough photos in photos array to start OpenSfM')
|
2019-04-22 19:14:39 +00:00
|
|
|
exit(1)
|
2015-11-27 16:51:21 +00:00
|
|
|
|
2019-04-25 15:40:45 +00:00
|
|
|
octx = OSFMContext(tree.opensfm)
|
2020-03-06 18:04:34 +00:00
|
|
|
octx.setup(args, tree.dataset_raw, photos, reconstruction=reconstruction, rerun=self.rerun())
|
|
|
|
octx.extract_metadata(self.rerun())
|
|
|
|
self.update_progress(20)
|
|
|
|
octx.feature_matching(self.rerun())
|
|
|
|
self.update_progress(30)
|
|
|
|
octx.reconstruct(self.rerun())
|
|
|
|
octx.extract_cameras(tree.path("cameras.json"), self.rerun())
|
|
|
|
self.update_progress(70)
|
2019-04-25 15:40:45 +00:00
|
|
|
|
2020-03-30 14:32:21 +00:00
|
|
|
if args.optimize_disk_space:
|
2020-08-24 14:33:12 +00:00
|
|
|
for folder in ["features", "matches", "exif", "reports"]:
|
|
|
|
folder_path = octx.path(folder)
|
|
|
|
if os.path.islink(folder_path):
|
|
|
|
os.unlink(folder_path)
|
|
|
|
else:
|
|
|
|
shutil.rmtree(folder_path)
|
2020-03-30 14:32:21 +00:00
|
|
|
|
2020-03-06 18:04:34 +00:00
|
|
|
# If we find a special flag file for split/merge we stop right here
|
|
|
|
if os.path.exists(octx.path("split_merge_stop_at_reconstruction.txt")):
|
|
|
|
log.ODM_INFO("Stopping OpenSfM early because we found: %s" % octx.path("split_merge_stop_at_reconstruction.txt"))
|
|
|
|
self.next_stage = None
|
|
|
|
return
|
2019-05-08 16:00:07 +00:00
|
|
|
|
2020-03-06 18:04:34 +00:00
|
|
|
if args.fast_orthophoto:
|
|
|
|
output_file = octx.path('reconstruction.ply')
|
|
|
|
elif args.use_opensfm_dense:
|
|
|
|
output_file = tree.opensfm_model
|
|
|
|
else:
|
|
|
|
output_file = tree.opensfm_reconstruction
|
2015-11-26 12:15:02 +00:00
|
|
|
|
2020-03-06 18:04:34 +00:00
|
|
|
updated_config_flag_file = octx.path('updated_config.txt')
|
2019-08-15 21:01:11 +00:00
|
|
|
|
2020-03-06 18:04:34 +00:00
|
|
|
# Make sure it's capped by the depthmap-resolution arg,
|
|
|
|
# since the undistorted images are used for MVS
|
|
|
|
outputs['undist_image_max_size'] = max(
|
|
|
|
gsd.image_max_size(photos, args.orthophoto_resolution, tree.opensfm_reconstruction, ignore_gsd=args.ignore_gsd, has_gcp=reconstruction.has_gcp()),
|
|
|
|
args.depthmap_resolution
|
|
|
|
)
|
2019-08-15 21:01:11 +00:00
|
|
|
|
2020-03-06 18:04:34 +00:00
|
|
|
if not io.file_exists(updated_config_flag_file) or self.rerun():
|
|
|
|
octx.update_config({'undistorted_image_max_size': outputs['undist_image_max_size']})
|
|
|
|
octx.touch(updated_config_flag_file)
|
2017-04-04 16:54:40 +00:00
|
|
|
|
2019-08-15 21:01:11 +00:00
|
|
|
# These will be used for texturing / MVS
|
2020-02-26 22:33:08 +00:00
|
|
|
if args.radiometric_calibration == "none":
|
|
|
|
octx.convert_and_undistort(self.rerun())
|
|
|
|
else:
|
|
|
|
def radiometric_calibrate(shot_id, image):
|
|
|
|
photo = reconstruction.get_photo(shot_id)
|
2020-03-06 18:04:34 +00:00
|
|
|
return multispectral.dn_to_reflectance(photo, image, use_sun_sensor=args.radiometric_calibration=="camera+sun")
|
2020-02-26 22:33:08 +00:00
|
|
|
|
|
|
|
octx.convert_and_undistort(self.rerun(), radiometric_calibrate)
|
2018-08-11 16:45:21 +00:00
|
|
|
|
2019-05-15 22:01:46 +00:00
|
|
|
self.update_progress(80)
|
|
|
|
|
2019-12-16 16:52:27 +00:00
|
|
|
if reconstruction.multi_camera:
|
2019-12-13 19:40:04 +00:00
|
|
|
# Dump band image lists
|
|
|
|
log.ODM_INFO("Multiple bands found")
|
|
|
|
for band in reconstruction.multi_camera:
|
|
|
|
log.ODM_INFO("Exporting %s band" % band['name'])
|
|
|
|
image_list_file = octx.path("image_list_%s.txt" % band['name'].lower())
|
|
|
|
|
|
|
|
if not io.file_exists(image_list_file) or self.rerun():
|
|
|
|
with open(image_list_file, "w") as f:
|
|
|
|
f.write("\n".join([p.filename for p in band['photos']]))
|
|
|
|
log.ODM_INFO("Wrote %s" % image_list_file)
|
|
|
|
else:
|
|
|
|
log.ODM_WARNING("Found a valid image list in %s for %s band" % (image_list_file, band['name']))
|
|
|
|
|
|
|
|
nvm_file = octx.path("undistorted", "reconstruction_%s.nvm" % band['name'].lower())
|
|
|
|
if not io.file_exists(nvm_file) or self.rerun():
|
|
|
|
octx.run('export_visualsfm --points --image_list "%s"' % image_list_file)
|
|
|
|
os.rename(tree.opensfm_reconstruction_nvm, nvm_file)
|
|
|
|
else:
|
|
|
|
log.ODM_WARNING("Found a valid NVM file in %s for %s band" % (nvm_file, band['name']))
|
2019-05-30 19:58:37 +00:00
|
|
|
|
2019-12-16 16:52:27 +00:00
|
|
|
if not io.file_exists(tree.opensfm_reconstruction_nvm) or self.rerun():
|
|
|
|
octx.run('export_visualsfm --points')
|
|
|
|
else:
|
|
|
|
log.ODM_WARNING('Found a valid OpenSfM NVM reconstruction file in: %s' %
|
|
|
|
tree.opensfm_reconstruction_nvm)
|
|
|
|
|
2019-05-30 19:58:37 +00:00
|
|
|
self.update_progress(85)
|
|
|
|
|
2019-04-28 16:20:03 +00:00
|
|
|
# Skip dense reconstruction if necessary and export
|
|
|
|
# sparse reconstruction instead
|
|
|
|
if args.fast_orthophoto:
|
|
|
|
if not io.file_exists(output_file) or self.rerun():
|
2019-05-10 13:38:18 +00:00
|
|
|
octx.run('export_ply --no-cameras')
|
2019-04-28 16:20:03 +00:00
|
|
|
else:
|
|
|
|
log.ODM_WARNING("Found a valid PLY reconstruction in %s" % output_file)
|
2019-04-27 19:51:13 +00:00
|
|
|
|
2019-04-28 16:20:03 +00:00
|
|
|
elif args.use_opensfm_dense:
|
|
|
|
if not io.file_exists(output_file) or self.rerun():
|
|
|
|
octx.run('compute_depthmaps')
|
|
|
|
else:
|
|
|
|
log.ODM_WARNING("Found a valid dense reconstruction in %s" % output_file)
|
2015-12-10 11:01:41 +00:00
|
|
|
|
2019-05-15 22:01:46 +00:00
|
|
|
self.update_progress(90)
|
|
|
|
|
2019-06-20 19:39:49 +00:00
|
|
|
if reconstruction.is_georeferenced() and (not io.file_exists(tree.opensfm_transformation) or self.rerun()):
|
|
|
|
octx.run('export_geocoords --transformation --proj \'%s\'' % reconstruction.georef.proj4())
|
2019-04-27 19:51:13 +00:00
|
|
|
else:
|
2020-03-30 14:32:21 +00:00
|
|
|
log.ODM_WARNING("Will skip exporting %s" % tree.opensfm_transformation)
|
2020-05-15 18:36:46 +00:00
|
|
|
|
2020-03-30 14:32:21 +00:00
|
|
|
if args.optimize_disk_space:
|
|
|
|
os.remove(octx.path("tracks.csv"))
|
|
|
|
os.remove(octx.path("undistorted", "tracks.csv"))
|
|
|
|
os.remove(octx.path("undistorted", "reconstruction.json"))
|
|
|
|
if io.dir_exists(octx.path("undistorted", "depthmaps")):
|
|
|
|
files = glob.glob(octx.path("undistorted", "depthmaps", "*.npz"))
|
|
|
|
for f in files:
|
|
|
|
os.remove(f)
|