kopia lustrzana https://github.com/OpenDroneMap/ODM
Point to 061 branch in OpenDroneMap/OpenSfM, undistort logic adaptation
Former-commit-id: 05d0706da1
pull/1161/head
rodzic
f02cbc7225
commit
3249a196fa
|
@ -8,8 +8,8 @@ ExternalProject_Add(${_proj_name}
|
||||||
STAMP_DIR ${_SB_BINARY_DIR}/stamp
|
STAMP_DIR ${_SB_BINARY_DIR}/stamp
|
||||||
#--Download step--------------
|
#--Download step--------------
|
||||||
DOWNLOAD_DIR ${SB_DOWNLOAD_DIR}
|
DOWNLOAD_DIR ${SB_DOWNLOAD_DIR}
|
||||||
GIT_REPOSITORY https://github.com/mapillary/OpenSfM/
|
GIT_REPOSITORY https://github.com/OpenDroneMap/OpenSfM/
|
||||||
GIT_TAG 00655397be3e27c490492ae01197bf66c51fa493
|
GIT_TAG 061
|
||||||
#--Update/Patch step----------
|
#--Update/Patch step----------
|
||||||
UPDATE_COMMAND git submodule update --init --recursive
|
UPDATE_COMMAND git submodule update --init --recursive
|
||||||
#--Configure step-------------
|
#--Configure step-------------
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import math
|
||||||
from repoze.lru import lru_cache
|
from repoze.lru import lru_cache
|
||||||
from opendm import log
|
from opendm import log
|
||||||
|
|
||||||
|
@ -20,6 +21,29 @@ def rounded_gsd(reconstruction_json, default_value=None, ndigits=0, ignore_gsd=F
|
||||||
return default_value
|
return default_value
|
||||||
|
|
||||||
|
|
||||||
|
def image_max_size(photos, target_resolution, reconstruction_json, gsd_error_estimate = 0.1, ignore_gsd=False):
|
||||||
|
"""
|
||||||
|
:param photos images database
|
||||||
|
:param target_resolution resolution the user wants have in cm / pixel
|
||||||
|
:param reconstruction_json path to OpenSfM's reconstruction.json
|
||||||
|
:param gsd_error_estimate percentage of estimated error in the GSD calculation to set an upper bound on resolution.
|
||||||
|
:param ignore_gsd if set to True, simply return the largest side of the largest image in the images database.
|
||||||
|
:return A dimension in pixels calculated by taking the image_scale_factor and applying it to the size of the largest image.
|
||||||
|
Returned value is never higher than the size of the largest side of the largest image.
|
||||||
|
"""
|
||||||
|
max_width = 0
|
||||||
|
max_height = 0
|
||||||
|
if ignore_gsd:
|
||||||
|
isf = 1.0
|
||||||
|
else:
|
||||||
|
isf = image_scale_factor(target_resolution, reconstruction_json, gsd_error_estimate)
|
||||||
|
|
||||||
|
for p in photos:
|
||||||
|
max_width = max(p.width, max_width)
|
||||||
|
max_height = max(p.height, max_height)
|
||||||
|
|
||||||
|
return int(math.ceil(max(max_width, max_height) * isf))
|
||||||
|
|
||||||
def image_scale_factor(target_resolution, reconstruction_json, gsd_error_estimate = 0.1):
|
def image_scale_factor(target_resolution, reconstruction_json, gsd_error_estimate = 0.1):
|
||||||
"""
|
"""
|
||||||
:param target_resolution resolution the user wants have in cm / pixel
|
:param target_resolution resolution the user wants have in cm / pixel
|
||||||
|
|
|
@ -3,6 +3,7 @@ OpenSfM related utils
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os, shutil, sys
|
import os, shutil, sys
|
||||||
|
import yaml
|
||||||
from opendm import io
|
from opendm import io
|
||||||
from opendm import log
|
from opendm import log
|
||||||
from opendm import system
|
from opendm import system
|
||||||
|
@ -85,6 +86,7 @@ class OSFMContext:
|
||||||
"depthmap_min_patch_sd: %s" % args.opensfm_depthmap_min_patch_sd,
|
"depthmap_min_patch_sd: %s" % args.opensfm_depthmap_min_patch_sd,
|
||||||
"depthmap_min_consistent_views: %s" % args.opensfm_depthmap_min_consistent_views,
|
"depthmap_min_consistent_views: %s" % args.opensfm_depthmap_min_consistent_views,
|
||||||
"optimize_camera_parameters: %s" % ('no' if args.use_fixed_camera_params else 'yes'),
|
"optimize_camera_parameters: %s" % ('no' if args.use_fixed_camera_params else 'yes'),
|
||||||
|
"undistorted_image_format: png" # mvs-texturing exhibits artifacts with JPG
|
||||||
]
|
]
|
||||||
|
|
||||||
if has_alt:
|
if has_alt:
|
||||||
|
@ -109,7 +111,7 @@ class OSFMContext:
|
||||||
|
|
||||||
# write config file
|
# write config file
|
||||||
log.ODM_DEBUG(config)
|
log.ODM_DEBUG(config)
|
||||||
config_filename = io.join_paths(self.opensfm_project_path, 'config.yaml')
|
config_filename = self.get_config_file_path()
|
||||||
with open(config_filename, 'w') as fout:
|
with open(config_filename, 'w') as fout:
|
||||||
fout.write("\n".join(config))
|
fout.write("\n".join(config))
|
||||||
|
|
||||||
|
@ -121,6 +123,9 @@ class OSFMContext:
|
||||||
else:
|
else:
|
||||||
log.ODM_WARNING("%s already exists, not rerunning OpenSfM setup" % list_path)
|
log.ODM_WARNING("%s already exists, not rerunning OpenSfM setup" % list_path)
|
||||||
|
|
||||||
|
def get_config_file_path(self):
|
||||||
|
return io.join_paths(self.opensfm_project_path, 'config.yaml')
|
||||||
|
|
||||||
def extract_metadata(self, rerun=False):
|
def extract_metadata(self, rerun=False):
|
||||||
metadata_dir = self.path("exif")
|
metadata_dir = self.path("exif")
|
||||||
if not io.dir_exists(metadata_dir) or rerun:
|
if not io.dir_exists(metadata_dir) or rerun:
|
||||||
|
@ -157,6 +162,23 @@ class OSFMContext:
|
||||||
def path(self, *paths):
|
def path(self, *paths):
|
||||||
return os.path.join(self.opensfm_project_path, *paths)
|
return os.path.join(self.opensfm_project_path, *paths)
|
||||||
|
|
||||||
|
def update_config(self, cfg_dict):
|
||||||
|
cfg_file = self.get_config_file_path()
|
||||||
|
log.ODM_DEBUG("Updating %s" % cfg_file)
|
||||||
|
if os.path.exists(cfg_file):
|
||||||
|
try:
|
||||||
|
with open(cfg_file) as fin:
|
||||||
|
cfg = yaml.safe_load(fin)
|
||||||
|
for k, v in cfg_dict.items():
|
||||||
|
cfg[k] = v
|
||||||
|
log.ODM_DEBUG("%s: %s" % (k, v))
|
||||||
|
with open(cfg_file, 'w') as fout:
|
||||||
|
fout.write(yaml.dump(cfg, default_flow_style=False))
|
||||||
|
except Exception as e:
|
||||||
|
log.ODM_WARNING("Cannot update configuration file %s: %s" % (cfg_file, str(e)))
|
||||||
|
else:
|
||||||
|
log.ODM_WARNING("Tried to update configuration, but %s does not exist." % cfg_file)
|
||||||
|
|
||||||
def save_absolute_image_list_to(self, file):
|
def save_absolute_image_list_to(self, file):
|
||||||
"""
|
"""
|
||||||
Writes a copy of the image_list.txt file and makes sure that all paths
|
Writes a copy of the image_list.txt file and makes sure that all paths
|
||||||
|
|
|
@ -42,29 +42,26 @@ class ODMOpenSfMStage(types.ODM_Stage):
|
||||||
else:
|
else:
|
||||||
output_file = tree.opensfm_reconstruction
|
output_file = tree.opensfm_reconstruction
|
||||||
|
|
||||||
# Always export VisualSFM's reconstruction and undistort images
|
octx.update_config({'undistorted_image_max_size': gsd.image_max_size(photos, args.orthophoto_resolution, tree.opensfm_reconstruction, ignore_gsd=args.ignore_gsd)})
|
||||||
# as we'll use these for texturing (after GSD estimation and resizing)
|
|
||||||
if not args.ignore_gsd:
|
|
||||||
image_scale = gsd.image_scale_factor(args.orthophoto_resolution, tree.opensfm_reconstruction)
|
|
||||||
else:
|
|
||||||
image_scale = 1.0
|
|
||||||
|
|
||||||
if not io.file_exists(tree.opensfm_reconstruction_nvm) or self.rerun():
|
|
||||||
octx.run('export_visualsfm --image_extension png --scale_focal %s' % image_scale)
|
|
||||||
else:
|
|
||||||
log.ODM_WARNING('Found a valid OpenSfM NVM reconstruction file in: %s' %
|
|
||||||
tree.opensfm_reconstruction_nvm)
|
|
||||||
|
|
||||||
# These will be used for texturing
|
# These will be used for texturing
|
||||||
undistorted_images_path = octx.path("undistorted")
|
undistorted_images_path = octx.path("undistorted")
|
||||||
|
|
||||||
if not io.dir_exists(undistorted_images_path) or self.rerun():
|
if not io.dir_exists(undistorted_images_path) or self.rerun():
|
||||||
octx.run('undistort --image_format png --image_scale %s' % image_scale)
|
octx.run('undistort')
|
||||||
else:
|
else:
|
||||||
log.ODM_WARNING("Found an undistorted directory in %s" % undistorted_images_path)
|
log.ODM_WARNING("Found an undistorted directory in %s" % undistorted_images_path)
|
||||||
|
|
||||||
self.update_progress(80)
|
self.update_progress(80)
|
||||||
|
|
||||||
|
if not io.file_exists(tree.opensfm_reconstruction_nvm) or self.rerun():
|
||||||
|
octx.run('export_visualsfm')
|
||||||
|
else:
|
||||||
|
log.ODM_WARNING('Found a valid OpenSfM NVM reconstruction file in: %s' %
|
||||||
|
tree.opensfm_reconstruction_nvm)
|
||||||
|
|
||||||
|
self.update_progress(85)
|
||||||
|
|
||||||
# Skip dense reconstruction if necessary and export
|
# Skip dense reconstruction if necessary and export
|
||||||
# sparse reconstruction instead
|
# sparse reconstruction instead
|
||||||
if args.fast_orthophoto:
|
if args.fast_orthophoto:
|
||||||
|
@ -74,11 +71,8 @@ class ODMOpenSfMStage(types.ODM_Stage):
|
||||||
log.ODM_WARNING("Found a valid PLY reconstruction in %s" % output_file)
|
log.ODM_WARNING("Found a valid PLY reconstruction in %s" % output_file)
|
||||||
|
|
||||||
elif args.use_opensfm_dense:
|
elif args.use_opensfm_dense:
|
||||||
# Undistort images at full scale in JPG
|
|
||||||
# (TODO: we could compare the size of the PNGs if they are < than depthmap_resolution
|
|
||||||
# and use those instead of re-exporting full resolution JPGs)
|
|
||||||
if not io.file_exists(output_file) or self.rerun():
|
if not io.file_exists(output_file) or self.rerun():
|
||||||
octx.run('undistort')
|
#octx.run('undistort') # TODO can we use PNGs here?
|
||||||
octx.run('compute_depthmaps')
|
octx.run('compute_depthmaps')
|
||||||
else:
|
else:
|
||||||
log.ODM_WARNING("Found a valid dense reconstruction in %s" % output_file)
|
log.ODM_WARNING("Found a valid dense reconstruction in %s" % output_file)
|
||||||
|
|
Ładowanie…
Reference in New Issue