kopia lustrzana https://github.com/OpenDroneMap/ODM
rodzic
494a441ef9
commit
5ec1b58a70
|
@ -17,7 +17,8 @@ class OSFMContext:
|
||||||
self.opensfm_project_path = opensfm_project_path
|
self.opensfm_project_path = opensfm_project_path
|
||||||
|
|
||||||
def run(self, command):
|
def run(self, command):
|
||||||
system.run('%s/bin/opensfm %s "%s"' %
|
# Use Python 2.x by default, otherwise OpenSfM uses Python 3.x
|
||||||
|
system.run('/usr/bin/env python2 %s/bin/opensfm %s "%s"' %
|
||||||
(context.opensfm_path, command, self.opensfm_project_path))
|
(context.opensfm_path, command, self.opensfm_project_path))
|
||||||
|
|
||||||
def is_reconstruction_done(self):
|
def is_reconstruction_done(self):
|
||||||
|
|
|
@ -32,6 +32,7 @@ class ODM_Photo:
|
||||||
self.longitude = None
|
self.longitude = None
|
||||||
self.altitude = None
|
self.altitude = None
|
||||||
self.band_name = 'RGB'
|
self.band_name = 'RGB'
|
||||||
|
self.band_index = 0
|
||||||
|
|
||||||
# parse values from metadata
|
# parse values from metadata
|
||||||
self.parse_exif_values(path_file)
|
self.parse_exif_values(path_file)
|
||||||
|
@ -41,9 +42,9 @@ class ODM_Photo:
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{} | camera: {} {} | dimensions: {} x {} | lat: {} | lon: {} | alt: {} | band: {}'.format(
|
return '{} | camera: {} {} | dimensions: {} x {} | lat: {} | lon: {} | alt: {} | band: {} ({})'.format(
|
||||||
self.filename, self.camera_make, self.camera_model, self.width, self.height,
|
self.filename, self.camera_make, self.camera_model, self.width, self.height,
|
||||||
self.latitude, self.longitude, self.altitude, self.band_name)
|
self.latitude, self.longitude, self.altitude, self.band_name, self.band_index)
|
||||||
|
|
||||||
def parse_exif_values(self, _path_file):
|
def parse_exif_values(self, _path_file):
|
||||||
# Disable exifread log
|
# Disable exifread log
|
||||||
|
@ -72,13 +73,21 @@ class ODM_Photo:
|
||||||
f.seek(0)
|
f.seek(0)
|
||||||
xmp = self.get_xmp(f)
|
xmp = self.get_xmp(f)
|
||||||
|
|
||||||
# Find band name (if available)
|
# Find band name and camera index (if available)
|
||||||
|
camera_index_tags = [
|
||||||
|
'DLS:SensorId', # Micasense
|
||||||
|
'@Camera:RigCameraIndex' # Parrot Sequoia
|
||||||
|
]
|
||||||
|
|
||||||
for tags in xmp:
|
for tags in xmp:
|
||||||
if 'Camera:BandName' in tags:
|
if 'Camera:BandName' in tags:
|
||||||
self.band_name = str(tags['Camera:BandName']).replace(" ", "")
|
self.band_name = str(tags['Camera:BandName']).replace(" ", "")
|
||||||
break
|
else:
|
||||||
|
for cit in camera_index_tags:
|
||||||
|
if cit in tags:
|
||||||
|
self.band_index = int(tags[cit])
|
||||||
self.width, self.height = get_image_size.get_image_size(_path_file)
|
self.width, self.height = get_image_size.get_image_size(_path_file)
|
||||||
|
print(self)
|
||||||
|
|
||||||
# From https://github.com/mapillary/OpenSfM/blob/master/opensfm/exif.py
|
# From https://github.com/mapillary/OpenSfM/blob/master/opensfm/exif.py
|
||||||
def get_xmp(self, file):
|
def get_xmp(self, file):
|
||||||
|
@ -128,24 +137,36 @@ class ODM_Reconstruction(object):
|
||||||
Looks at the reconstruction photos and determines if this
|
Looks at the reconstruction photos and determines if this
|
||||||
is a single or multi-camera setup.
|
is a single or multi-camera setup.
|
||||||
"""
|
"""
|
||||||
mc = {}
|
band_photos = {}
|
||||||
|
band_indexes = {}
|
||||||
|
|
||||||
for p in self.photos:
|
for p in self.photos:
|
||||||
if not p.band_name in mc:
|
if not p.band_name in band_photos:
|
||||||
mc[p.band_name] = []
|
band_photos[p.band_name] = []
|
||||||
mc[p.band_name].append(p)
|
if not p.band_name in band_indexes:
|
||||||
|
band_indexes[p.band_name] = p.band_index
|
||||||
|
|
||||||
|
band_photos[p.band_name].append(p)
|
||||||
|
|
||||||
bands_count = len(mc)
|
bands_count = len(band_photos)
|
||||||
if bands_count >= 2 and bands_count <= 8:
|
if bands_count >= 2 and bands_count <= 8:
|
||||||
# Validate that all bands have the same number of images,
|
# Validate that all bands have the same number of images,
|
||||||
# otherwise this is not a multi-camera setup
|
# otherwise this is not a multi-camera setup
|
||||||
img_per_band = len(mc[p.band_name])
|
img_per_band = len(band_photos[p.band_name])
|
||||||
for band in mc:
|
for band in band_photos:
|
||||||
if len(mc[band]) != img_per_band:
|
if len(band_photos[band]) != img_per_band:
|
||||||
log.ODM_ERROR("Multi-camera setup detected, but band \"%s\" (identified from \"%s\") has only %s images (instead of %s), perhaps images are missing or are corrupted. Please include all necessary files to process all bands and try again." % (band, mc[band][0].filename, len(mc[band]), img_per_band))
|
log.ODM_ERROR("Multi-camera setup detected, but band \"%s\" (identified from \"%s\") has only %s images (instead of %s), perhaps images are missing or are corrupted. Please include all necessary files to process all bands and try again." % (band, band_photos[band][0].filename, len(band_photos[band]), img_per_band))
|
||||||
raise RuntimeError("Invalid multi-camera images")
|
raise RuntimeError("Invalid multi-camera images")
|
||||||
|
|
||||||
|
mc = []
|
||||||
|
for band_name in band_indexes:
|
||||||
|
mc.append({'name': band_name, 'photos': band_photos[band_name]})
|
||||||
|
|
||||||
|
# Sort by band index
|
||||||
|
mc.sort(key=lambda x: band_indexes[x['name']])
|
||||||
|
|
||||||
return mc
|
return mc
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def is_georeferenced(self):
|
def is_georeferenced(self):
|
||||||
|
@ -187,7 +208,7 @@ class ODM_Reconstruction(object):
|
||||||
log.ODM_INFO("GCP file already exist: %s" % output_gcp_file)
|
log.ODM_INFO("GCP file already exist: %s" % output_gcp_file)
|
||||||
self.gcp = GCPFile(output_gcp_file)
|
self.gcp = GCPFile(output_gcp_file)
|
||||||
|
|
||||||
self.georef = ODM_GeoRef.FromCoordsFile(output_coords_file)
|
self.georef = ODM_GeoRef.Froband_photosoordsFile(output_coords_file)
|
||||||
return self.georef
|
return self.georef
|
||||||
|
|
||||||
def georeference_with_gps(self, images_path, output_coords_file, rerun=False):
|
def georeference_with_gps(self, images_path, output_coords_file, rerun=False):
|
||||||
|
@ -197,7 +218,7 @@ class ODM_Reconstruction(object):
|
||||||
else:
|
else:
|
||||||
log.ODM_INFO("Coordinates file already exist: %s" % output_coords_file)
|
log.ODM_INFO("Coordinates file already exist: %s" % output_coords_file)
|
||||||
|
|
||||||
self.georef = ODM_GeoRef.FromCoordsFile(output_coords_file)
|
self.georef = ODM_GeoRef.Froband_photosoordsFile(output_coords_file)
|
||||||
except:
|
except:
|
||||||
log.ODM_WARNING('Could not generate coordinates file. An orthophoto will not be generated.')
|
log.ODM_WARNING('Could not generate coordinates file. An orthophoto will not be generated.')
|
||||||
|
|
||||||
|
@ -217,7 +238,7 @@ class ODM_GeoRef(object):
|
||||||
return ODM_GeoRef(CRS.from_proj4(projstring))
|
return ODM_GeoRef(CRS.from_proj4(projstring))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def FromCoordsFile(coords_file):
|
def Froband_photosoordsFile(coords_file):
|
||||||
# check for coordinate file existence
|
# check for coordinate file existence
|
||||||
if not io.file_exists(coords_file):
|
if not io.file_exists(coords_file):
|
||||||
log.ODM_WARNING('Could not find file %s' % coords_file)
|
log.ODM_WARNING('Could not find file %s' % coords_file)
|
||||||
|
|
|
@ -2,9 +2,10 @@ import unittest
|
||||||
from opendm import types
|
from opendm import types
|
||||||
|
|
||||||
class ODMPhotoMock:
|
class ODMPhotoMock:
|
||||||
def __init__(self, filename, band_name):
|
def __init__(self, filename, band_name, band_index):
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.band_name = band_name
|
self.band_name = band_name
|
||||||
|
self.band_index = band_index
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s (%s)" % (self.filename, self.band_name)
|
return "%s (%s)" % (self.filename, self.band_name)
|
||||||
|
@ -18,29 +19,30 @@ class TestTypes(unittest.TestCase):
|
||||||
|
|
||||||
def test_reconstruction(self):
|
def test_reconstruction(self):
|
||||||
# Multi camera setup
|
# Multi camera setup
|
||||||
micasa_redsense_files = [('IMG_0298_1.tif', 'Red'), ('IMG_0298_2.tif', 'Green'), ('IMG_0298_3.tif', 'Blue'), ('IMG_0298_4.tif', 'NIR'), ('IMG_0298_5.tif', 'Rededge'),
|
micasa_redsense_files = [('IMG_0298_1.tif', 'Red', 1), ('IMG_0298_2.tif', 'Green', 2), ('IMG_0298_3.tif', 'Blue', 3), ('IMG_0298_4.tif', 'NIR', 4), ('IMG_0298_5.tif', 'Rededge', 5),
|
||||||
('IMG_0299_1.tif', 'Red'), ('IMG_0299_2.tif', 'Green'), ('IMG_0299_3.tif', 'Blue'), ('IMG_0299_4.tif', 'NIR'), ('IMG_0299_5.tif', 'Rededge'),
|
('IMG_0299_1.tif', 'Red', 1), ('IMG_0299_2.tif', 'Green', 2), ('IMG_0299_3.tif', 'Blue', 3), ('IMG_0299_4.tif', 'NIR', 4), ('IMG_0299_5.tif', 'Rededge', 5),
|
||||||
('IMG_0300_1.tif', 'Red'), ('IMG_0300_2.tif', 'Green'), ('IMG_0300_3.tif', 'Blue'), ('IMG_0300_4.tif', 'NIR'), ('IMG_0300_5.tif', 'Rededge')]
|
('IMG_0300_1.tif', 'Red', 1), ('IMG_0300_2.tif', 'Green', 2), ('IMG_0300_3.tif', 'Blue', 3), ('IMG_0300_4.tif', 'NIR', 4), ('IMG_0300_5.tif', 'Rededge', 5)]
|
||||||
photos = [ODMPhotoMock(f, b) for f, b in micasa_redsense_files]
|
photos = [ODMPhotoMock(f, b, i) for f, b, i in micasa_redsense_files]
|
||||||
recon = types.ODM_Reconstruction(photos)
|
recon = types.ODM_Reconstruction(photos)
|
||||||
|
|
||||||
self.assertTrue(recon.multi_camera is not None)
|
self.assertTrue(recon.multi_camera is not None)
|
||||||
|
|
||||||
# Found all 5 bands
|
# Found all 5 bands
|
||||||
for b in ["Red", "Blue", "Green", "NIR", "Rededge"]:
|
bands = ["Red", "Green", "Blue", "NIR", "Rededge"]
|
||||||
self.assertTrue(b in recon.multi_camera)
|
for i in range(len(bands)):
|
||||||
self.assertTrue([p.filename for p in recon.multi_camera["Red"]] == ['IMG_0298_1.tif', 'IMG_0299_1.tif', 'IMG_0300_1.tif'])
|
self.assertEqual(bands[i], recon.multi_camera[i]['name'])
|
||||||
|
self.assertTrue([p.filename for p in recon.multi_camera[0]['photos']] == ['IMG_0298_1.tif', 'IMG_0299_1.tif', 'IMG_0300_1.tif'])
|
||||||
|
|
||||||
# Missing a file
|
# Missing a file
|
||||||
micasa_redsense_files = [('IMG_0298_1.tif', 'Red'), ('IMG_0298_2.tif', 'Green'), ('IMG_0298_3.tif', 'Blue'), ('IMG_0298_4.tif', 'NIR'), ('IMG_0298_5.tif', 'Rededge'),
|
micasa_redsense_files = [('IMG_0298_1.tif', 'Red', 1), ('IMG_0298_2.tif', 'Green', 2), ('IMG_0298_3.tif', 'Blue', 3), ('IMG_0298_4.tif', 'NIR', 4), ('IMG_0298_5.tif', 'Rededge', 5),
|
||||||
('IMG_0299_2.tif', 'Green'), ('IMG_0299_3.tif', 'Blue'), ('IMG_0299_4.tif', 'NIR'), ('IMG_0299_5.tif', 'Rededge'),
|
('IMG_0299_2.tif', 'Green', 2), ('IMG_0299_3.tif', 'Blue', 3), ('IMG_0299_4.tif', 'NIR', 4), ('IMG_0299_5.tif', 'Rededge', 5),
|
||||||
('IMG_0300_1.tif', 'Red'), ('IMG_0300_2.tif', 'Green'), ('IMG_0300_3.tif', 'Blue'), ('IMG_0300_4.tif', 'NIR'), ('IMG_0300_5.tif', 'Rededge')]
|
('IMG_0300_1.tif', 'Red', 1), ('IMG_0300_2.tif', 'Green', 2), ('IMG_0300_3.tif', 'Blue', 3), ('IMG_0300_4.tif', 'NIR', 4), ('IMG_0300_5.tif', 'Rededge', 5)]
|
||||||
photos = [ODMPhotoMock(f, b) for f,b in micasa_redsense_files]
|
photos = [ODMPhotoMock(f, b, i) for f,b,i in micasa_redsense_files]
|
||||||
self.assertRaises(RuntimeError, types.ODM_Reconstruction, photos)
|
self.assertRaises(RuntimeError, types.ODM_Reconstruction, photos)
|
||||||
|
|
||||||
# Single camera
|
# Single camera
|
||||||
dji_files = ['DJI_0018.JPG','DJI_0019.JPG','DJI_0020.JPG','DJI_0021.JPG','DJI_0022.JPG','DJI_0023.JPG']
|
dji_files = ['DJI_0018.JPG','DJI_0019.JPG','DJI_0020.JPG','DJI_0021.JPG','DJI_0022.JPG','DJI_0023.JPG']
|
||||||
photos = [ODMPhotoMock(f, 'RGB') for f in dji_files]
|
photos = [ODMPhotoMock(f, 'RGB', 0) for f in dji_files]
|
||||||
recon = types.ODM_Reconstruction(photos)
|
recon = types.ODM_Reconstruction(photos)
|
||||||
self.assertTrue(recon.multi_camera is None)
|
self.assertTrue(recon.multi_camera is None)
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue