diff --git a/opendm/nvm.py b/opendm/nvm.py new file mode 100644 index 00000000..d3c07d99 --- /dev/null +++ b/opendm/nvm.py @@ -0,0 +1,41 @@ +import os +from opendm import log + +def replace_nvm_images(src_nvm_file, img_map, dst_nvm_file): + """ + Create a new NVM file from an existing NVM file + replacing the image references based on img_map + where img_map is a dict { "old_image" --> "new_image" } (filename only). + The function does not write the points information (they are discarded) + """ + + with open(src_nvm_file) as f: + lines = list(map(str.strip, f.read().split("\n"))) + + # Quick check + if len(lines) < 3 or lines[0] != "NVM_V3" or lines[1].strip() != "": + raise Exception("%s does not seem to be a valid NVM file" % src_nvm_file) + + num_images = int(lines[2]) + entries = [] + + for l in lines[3:3+num_images]: + image_path, *p = l.split(" ") + + dir_name = os.path.dirname(image_path) + file_name = os.path.basename(image_path) + + new_filename = img_map.get(file_name) + if new_filename is not None: + entries.append("%s %s" % (os.path.join(dir_name, new_filename), " ".join(p))) + else: + log.ODM_WARNING("Cannot find %s in image map for %s" % (file_name, dst_nvm_file)) + + if num_images != len(entries): + raise Exception("Cannot write %s, not all band images have been matched" % dst_nvm_file) + + with open(dst_nvm_file, "w") as f: + f.write("NVM_V3\n\n%s\n" % len(entries)) + f.write("\n".join(entries)) + f.write("\n\n0\n0\n\n0") + \ No newline at end of file diff --git a/stages/mvstex.py b/stages/mvstex.py index 905dc393..da0ab2a8 100644 --- a/stages/mvstex.py +++ b/stages/mvstex.py @@ -25,7 +25,8 @@ class ODMMvsTexStage(types.ODM_Stage): 'out_dir': os.path.join(tree.odm_texturing, subdir), 'model': tree.odm_mesh, 'nadir': False, - 'nvm_file': nvm_file + 'nvm_file': nvm_file, + 'labeling_file': os.path.join(tree.odm_texturing, "odm_textured_model_labeling.vec") if subdir else None }] if not args.use_3dmesh: @@ -33,7 +34,8 @@ class ODMMvsTexStage(types.ODM_Stage): 'out_dir': os.path.join(tree.odm_25dtexturing, subdir), 'model': tree.odm_25dmesh, 'nadir': True, - 'nvm_file': nvm_file + 'nvm_file': nvm_file, + 'labeling_file': os.path.join(tree.odm_25dtexturing, "odm_textured_model_labeling.vec") if subdir else None }] if reconstruction.multi_camera: @@ -81,7 +83,9 @@ class ODMMvsTexStage(types.ODM_Stage): 'skipLocalSeamLeveling': skipLocalSeamLeveling, 'toneMapping': self.params.get('tone_mapping'), 'nadirMode': nadir, - 'nvm_file': r['nvm_file'] + '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') @@ -95,10 +99,11 @@ class ODMMvsTexStage(types.ODM_Stage): system.run('{bin} {nvm_file} {model} {out_dir} ' '-d {dataTerm} -o {outlierRemovalType} ' '-t {toneMapping} ' - '--no_intermediate_results ' + '{intermediate} ' '{skipGlobalSeamLeveling} ' '{skipLocalSeamLeveling} ' - '{nadirMode}'.format(**kwargs)) + '{nadirMode} ' + '{labelingFile} '.format(**kwargs)) progress += progress_per_run self.update_progress(progress) diff --git a/stages/openmvs.py b/stages/openmvs.py index 26f1f7ed..fdddcdf9 100644 --- a/stages/openmvs.py +++ b/stages/openmvs.py @@ -29,12 +29,6 @@ class ODMOpenMVSStage(types.ODM_Stage): # export reconstruction from opensfm octx = OSFMContext(tree.opensfm) cmd = 'export_openmvs' - if reconstruction.multi_camera: - # Export only the primary band - primary_band = get_primary_band_name(reconstruction.multi_camera, args.primary_band) - - image_list = os.path.join(tree.opensfm, "image_list_%s.txt" % primary_band.lower()) - cmd += ' --image_list "%s"' % image_list octx.run(cmd) self.update_progress(10) diff --git a/stages/run_opensfm.py b/stages/run_opensfm.py index e1021453..84cdf2b4 100644 --- a/stages/run_opensfm.py +++ b/stages/run_opensfm.py @@ -13,6 +13,7 @@ from opendm import types from opendm.utils import get_depthmap_resolution from opendm.osfm import OSFMContext from opendm import multispectral +from opendm import nvm class ODMOpenSfMStage(types.ODM_Stage): def process(self, args, outputs): @@ -127,27 +128,10 @@ class ODMOpenSfMStage(types.ODM_Stage): self.update_progress(80) if reconstruction.multi_camera: - # 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'])) - octx.restore_reconstruction_backup() + + # Undistort primary band and write undistorted + # reconstruction.json, tracks.csv octx.convert_and_undistort(self.rerun(), undistort_callback, runId='primary') if not io.file_exists(tree.opensfm_reconstruction_nvm) or self.rerun(): @@ -155,6 +139,29 @@ class ODMOpenSfMStage(types.ODM_Stage): else: log.ODM_WARNING('Found a valid OpenSfM NVM reconstruction file in: %s' % tree.opensfm_reconstruction_nvm) + + if reconstruction.multi_camera: + log.ODM_INFO("Multiple bands found") + + # Write NVM files for the various bands + for band in reconstruction.multi_camera: + nvm_file = octx.path("undistorted", "reconstruction_%s.nvm" % band['name'].lower()) + + img_map = {} + for fname in p2s: + + # Primary band maps to itself + if band['name'] == primary_band_name: + img_map[fname + '.tif'] = fname + '.tif' + else: + band_filename = next((p.filename for p in p2s[fname] if p.band_name == band['name']), None) + + if band_filename is not None: + img_map[fname + '.tif'] = band_filename + '.tif' + else: + log.ODM_WARNING("Cannot find %s band equivalent for %s" % (band, fname)) + + nvm.replace_nvm_images(tree.opensfm_reconstruction_nvm, img_map, nvm_file) self.update_progress(85)