kopia lustrzana https://github.com/OpenDroneMap/ODM
Started writing new multispectral alignment algorithm
rodzic
a2040b2274
commit
509035d5e9
|
@ -156,6 +156,15 @@ def config(argv=None, parser=None):
|
|||
help=('Set feature extraction quality. Higher quality generates better features, but requires more memory and takes longer. '
|
||||
'Can be one of: %(choices)s. Default: '
|
||||
'%(default)s'))
|
||||
|
||||
parser.add_argument('--feature-matcher',
|
||||
metavar='<string>',
|
||||
action=StoreValue,
|
||||
default='flann',
|
||||
choices=['flann', 'bow'],
|
||||
help=('Set feature matcher algorithm. FLANN is more robust, but slower. BOW is much faster, but can miss some valid matches. '
|
||||
'Can be one of: %(choices)s. Default: '
|
||||
'%(default)s'))
|
||||
|
||||
parser.add_argument('--matcher-neighbors',
|
||||
metavar='<integer>',
|
||||
|
@ -755,6 +764,15 @@ def config(argv=None, parser=None):
|
|||
'points will be re-classified and gaps will be filled. Useful for generating DTMs. '
|
||||
'Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--primary-band',
|
||||
metavar='<string>',
|
||||
action=StoreValue,
|
||||
default="auto",
|
||||
type=str,
|
||||
help=('When processing multispectral datasets, you can specify the name of the primary band that will be used for reconstruction. '
|
||||
'It\'s recommended to choose a band which has sharp details and is in focus. '
|
||||
'Default: %(default)s'))
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
# check that the project path setting has been set properly
|
||||
|
|
|
@ -150,4 +150,19 @@ def compute_irradiance(photo, use_sun_sensor=True):
|
|||
elif use_sun_sensor:
|
||||
log.ODM_WARNING("No sun sensor values found for %s" % photo.filename)
|
||||
|
||||
return 1.0
|
||||
return 1.0
|
||||
|
||||
def get_photos_by_band(multi_camera, band_name):
|
||||
if len(multi_camera) < 1:
|
||||
raise Exception("Invalid multi_camera list")
|
||||
|
||||
# multi_camera is already sorted by band_index
|
||||
if band_name == "auto":
|
||||
return multi_camera[0]['photos']
|
||||
|
||||
for band in multi_camera:
|
||||
if band['name'].lower() == band_name.lower():
|
||||
return band['photos']
|
||||
|
||||
logger.ODM_WARNING("Cannot find band name \"%s\", will use \"auto\" instead" % band_name)
|
||||
return multi_camera[0]['photos']
|
||||
|
|
|
@ -15,6 +15,7 @@ from opensfm.large import metadataset
|
|||
from opensfm.large import tools
|
||||
from opensfm.actions import undistort
|
||||
from opensfm.dataset import DataSet
|
||||
from opendm.multispectral import get_photos_by_band
|
||||
|
||||
class OSFMContext:
|
||||
def __init__(self, opensfm_project_path):
|
||||
|
@ -68,6 +69,14 @@ class OSFMContext:
|
|||
list_path = os.path.join(self.opensfm_project_path, 'image_list.txt')
|
||||
if not io.file_exists(list_path) or rerun:
|
||||
|
||||
if reconstruction.multi_camera:
|
||||
photos = get_photos_by_band(reconstruction.multi_camera, args.primary_band)
|
||||
if len(photos) < 1:
|
||||
raise Exception("Not enough images in selected band %s" % args.primary_band.lower())
|
||||
logger.ODM_INFO("Reconstruction will use %s images from %s band" % (len(photos), args.primary_band.lower()))
|
||||
else:
|
||||
photos = reconstruction.photos
|
||||
|
||||
# create file list
|
||||
has_alt = True
|
||||
has_gps = False
|
||||
|
@ -77,6 +86,7 @@ class OSFMContext:
|
|||
has_alt = False
|
||||
if photo.latitude is not None and photo.longitude is not None:
|
||||
has_gps = True
|
||||
|
||||
fout.write('%s\n' % os.path.join(images_path, photo.filename))
|
||||
|
||||
# check for image_groups.txt (split-merge)
|
||||
|
@ -95,16 +105,9 @@ class OSFMContext:
|
|||
except Exception as e:
|
||||
log.ODM_WARNING("Cannot set camera_models_overrides.json: %s" % str(e))
|
||||
|
||||
use_bow = False
|
||||
use_bow = args.feature_matcher == "bow"
|
||||
feature_type = "SIFT"
|
||||
|
||||
matcher_neighbors = args.matcher_neighbors
|
||||
if matcher_neighbors != 0 and reconstruction.multi_camera is not None:
|
||||
matcher_neighbors *= len(reconstruction.multi_camera)
|
||||
log.ODM_INFO("Increasing matcher neighbors to %s to accomodate multi-camera setup" % matcher_neighbors)
|
||||
log.ODM_INFO("Multi-camera setup, using BOW matching")
|
||||
use_bow = True
|
||||
|
||||
# GPSDOP override if we have GPS accuracy information (such as RTK)
|
||||
if 'gps_accuracy_is_set' in args:
|
||||
log.ODM_INFO("Forcing GPS DOP to %s for all images" % args.gps_accuracy)
|
||||
|
@ -188,8 +191,7 @@ class OSFMContext:
|
|||
"undistorted_image_format: tif",
|
||||
"bundle_outlier_filtering_type: AUTO",
|
||||
"align_orientation_prior: vertical",
|
||||
"triangulation_type: ROBUST",
|
||||
"bundle_common_position_constraints: %s" % ('no' if reconstruction.multi_camera is None else 'yes'),
|
||||
"triangulation_type: ROBUST"
|
||||
]
|
||||
|
||||
if args.camera_lens != 'auto':
|
||||
|
|
|
@ -25,7 +25,7 @@ class ODMOpenSfMStage(types.ODM_Stage):
|
|||
exit(1)
|
||||
|
||||
octx = OSFMContext(tree.opensfm)
|
||||
octx.setup(args, tree.dataset_raw, photos, reconstruction=reconstruction, rerun=self.rerun())
|
||||
octx.setup(args, tree.dataset_raw, reconstruction=reconstruction, rerun=self.rerun())
|
||||
octx.extract_metadata(self.rerun())
|
||||
self.update_progress(20)
|
||||
octx.feature_matching(self.rerun())
|
||||
|
@ -48,13 +48,6 @@ class ODMOpenSfMStage(types.ODM_Stage):
|
|||
self.next_stage = None
|
||||
return
|
||||
|
||||
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
|
||||
|
||||
updated_config_flag_file = octx.path('updated_config.txt')
|
||||
|
||||
# Make sure it's capped by the depthmap-resolution arg,
|
||||
|
@ -68,15 +61,29 @@ class ODMOpenSfMStage(types.ODM_Stage):
|
|||
octx.update_config({'undistorted_image_max_size': outputs['undist_image_max_size']})
|
||||
octx.touch(updated_config_flag_file)
|
||||
|
||||
# These will be used for texturing / MVS
|
||||
if args.radiometric_calibration == "none":
|
||||
octx.convert_and_undistort(self.rerun())
|
||||
else:
|
||||
def radiometric_calibrate(shot_id, image):
|
||||
photo = reconstruction.get_photo(shot_id)
|
||||
return multispectral.dn_to_reflectance(photo, image, use_sun_sensor=args.radiometric_calibration=="camera+sun")
|
||||
# Undistorted images will be used for texturing / MVS
|
||||
undistort_pipeline = []
|
||||
|
||||
octx.convert_and_undistort(self.rerun(), radiometric_calibrate)
|
||||
def undistort_callback(shot_id, image):
|
||||
for func in undistort_pipeline:
|
||||
image = func(shot_id, image)
|
||||
return image
|
||||
|
||||
def radiometric_calibrate(shot_id, image):
|
||||
photo = reconstruction.get_photo(shot_id)
|
||||
return multispectral.dn_to_reflectance(photo, image, use_sun_sensor=args.radiometric_calibration=="camera+sun")
|
||||
|
||||
def align_to_primary_band(shot_id, image):
|
||||
# TODO
|
||||
return image
|
||||
|
||||
if args.radiometric_calibration != "none"
|
||||
undistort_pipeline.append(radiometric_calibrate)
|
||||
|
||||
if reconstruction.multi_camera:
|
||||
undistort_pipeline.append(align_to_primary_band)
|
||||
|
||||
octx.convert_and_undistort(self.rerun(), undistort_callback)
|
||||
|
||||
self.update_progress(80)
|
||||
|
||||
|
@ -94,6 +101,7 @@ class ODMOpenSfMStage(types.ODM_Stage):
|
|||
else:
|
||||
log.ODM_WARNING("Found a valid image list in %s for %s band" % (image_list_file, band['name']))
|
||||
|
||||
# TODO!! CHANGE
|
||||
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)
|
||||
|
@ -112,12 +120,16 @@ class ODMOpenSfMStage(types.ODM_Stage):
|
|||
# Skip dense reconstruction if necessary and export
|
||||
# sparse reconstruction instead
|
||||
if args.fast_orthophoto:
|
||||
output_file = octx.path('reconstruction.ply')
|
||||
|
||||
if not io.file_exists(output_file) or self.rerun():
|
||||
octx.run('export_ply --no-cameras')
|
||||
else:
|
||||
log.ODM_WARNING("Found a valid PLY reconstruction in %s" % output_file)
|
||||
|
||||
elif args.use_opensfm_dense:
|
||||
output_file = tree.opensfm_model
|
||||
|
||||
if not io.file_exists(output_file) or self.rerun():
|
||||
octx.run('compute_depthmaps')
|
||||
else:
|
||||
|
|
|
@ -50,7 +50,7 @@ class ODMSplitStage(types.ODM_Stage):
|
|||
"submodel_overlap: %s" % args.split_overlap,
|
||||
]
|
||||
|
||||
octx.setup(args, tree.dataset_raw, photos, reconstruction=reconstruction, append_config=config, rerun=self.rerun())
|
||||
octx.setup(args, tree.dataset_raw, reconstruction=reconstruction, append_config=config, rerun=self.rerun())
|
||||
octx.extract_metadata(self.rerun())
|
||||
|
||||
self.update_progress(5)
|
||||
|
|
Ładowanie…
Reference in New Issue