Merge pull request #1015 from pierotofy/osfm-update

Upgraded opensfm code, added new deps
pull/1025/head
Piero Toffanin 2019-09-09 14:52:16 -04:00 zatwierdzone przez GitHub
commit d9e3959124
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
15 zmienionych plików z 102 dodań i 120 usunięć

Wyświetl plik

@ -50,9 +50,7 @@ RUN apt-get install --no-install-recommends -y \
python-dev \
python-gdal \
python-matplotlib \
python-networkx \
python-pip \
python-pyproj \
python-software-properties \
python-wheel \
swig2.0 \
@ -67,18 +65,20 @@ RUN pip install -U \
exifread \
gpxpy \
loky \
numpy==1.15.4 \
psutil \
pyproj \
PyYAML==3.13 \
repoze.lru \
scipy==1.2.1 \
shapely \
xmltodict \
rasterio \
attrs==19.1.0 \
pyodm==1.5.2b1 \
Pillow
Pillow \
networkx \
scipy==1.2.1 \
numpy==1.15.4 \
shapely \
pyproj \
psutil \
joblib
RUN pip install --upgrade cryptography && python -m easy_install --upgrade pyOpenSSL

Wyświetl plik

@ -140,7 +140,7 @@ externalproject_add(mve
externalproject_add(poissonrecon
GIT_REPOSITORY https://github.com/mkazhdan/PoissonRecon.git
GIT_TAG 20b17f5fb49312a4f765a6bcae55d9c4919932f3
GIT_TAG ce5005ae3094d902d551a65a8b3131e06f45e7cf
SOURCE_DIR ${SB_SOURCE_DIR}/PoissonRecon
UPDATE_COMMAND ""
CONFIGURE_COMMAND ""

Wyświetl plik

@ -7,8 +7,7 @@ ExternalProject_Add(${_proj_name}
STAMP_DIR ${_SB_BINARY_DIR}/stamp
#--Download step--------------
DOWNLOAD_DIR ${SB_DOWNLOAD_DIR}
URL https://github.com/Itseez/opencv/archive/2.4.11.zip
URL_MD5 b517e83489c709eee1d8be76b16976a7
URL https://github.com/opencv/opencv/archive/3.4.6.zip
#--Update/Patch step----------
UPDATE_COMMAND ""
#--Configure step-------------

Wyświetl plik

@ -9,7 +9,7 @@ ExternalProject_Add(${_proj_name}
#--Download step--------------
DOWNLOAD_DIR ${SB_DOWNLOAD_DIR}
GIT_REPOSITORY https://github.com/OpenDroneMap/OpenSfM/
GIT_TAG 070
GIT_TAG 090
#--Update/Patch step----------
UPDATE_COMMAND git submodule update --init --recursive
#--Configure step-------------

Wyświetl plik

@ -1 +1 @@
0.8.2
0.9.0

Wyświetl plik

@ -28,7 +28,11 @@ install() {
libjsoncpp-dev \
python-gdal \
grass-core \
libssl-dev
libssl-dev \
liblas-bin \
swig2.0 \
python-wheel \
libboost-log-dev
echo "Getting CMake 3.1 for MVS-Texturing"
apt-get install -y software-properties-common python-software-properties
@ -42,7 +46,6 @@ install() {
libavformat-dev \
libswscale-dev \
python-dev \
python-numpy \
libtbb2 \
libtbb-dev \
libjpeg-dev \
@ -59,18 +62,15 @@ install() {
echo "Removing libdc1394-22-dev due to python opencv issue"
apt-get remove libdc1394-22-dev
## Installing OpenSfM Requisites
echo "Installing OpenSfM Dependencies"
apt-get install -y -qq python-networkx \
libgoogle-glog-dev \
apt-get install -y -qq libgoogle-glog-dev \
libsuitesparse-dev \
libboost-filesystem-dev \
libboost-iostreams-dev \
libboost-regex-dev \
libboost-python-dev \
libboost-date-time-dev \
libboost-thread-dev \
python-pyproj
libboost-thread-dev
pip install -U PyYAML==3.13 \
exifread \
@ -82,25 +82,19 @@ install() {
rasterio \
attrs==19.1.0 \
pyodm==1.5.2b1 \
Pillow
Pillow \
networkx \
scipy==1.2.1 \
numpy==1.15.4 \
shapely \
pyproj \
psutil \
joblib
# Fix: /usr/local/lib/python2.7/dist-packages/requests/__init__.py:83: RequestsDependencyWarning: Old version of cryptography ([1, 2, 3]) may cause slowdown.
pip install --upgrade cryptography
python -m easy_install --upgrade pyOpenSSL
echo "Installing OpenDroneMap Dependencies"
apt-get install -y -qq python-scipy \
liblas-bin
echo "Installing lidar2dems Dependencies"
apt-get install -y -qq swig2.0 \
python-wheel \
libboost-log-dev
echo "Installing split-merge Dependencies"
pip install -U scipy==1.2.1 numpy==1.15.4 shapely pyproj psutil
echo "Compiling SuperBuild"
cd ${RUNPATH}/SuperBuild
mkdir -p build && cd build

Wyświetl plik

@ -70,9 +70,9 @@ def config():
metavar='<integer>',
default=2048,
type=int,
help='Resizes images by the largest side for feature extraction. '
help='Resizes images by the largest side for feature extraction purposes only. '
'Set to -1 to disable. This does not affect the final orthophoto '
' resolution quality. Default: %(default)s')
' resolution quality and will not resize the original images. Default: %(default)s')
parser.add_argument('--end-with', '-e',
metavar='<string>',
@ -149,6 +149,16 @@ def config():
'Can be specified either as path to a cameras.json file or as a '
'JSON string representing the contents of a '
'cameras.json file. Default: %(default)s')
parser.add_argument('--camera-lens',
metavar='<string>',
default='auto',
choices=['auto', 'perspective', 'brown', 'fisheye', 'spherical'],
help=('Set a camera projection type. Manually setting a value '
'can help improve geometric undistortion. By default the application '
'tries to determine a lens type from the images metadata. Can be '
'set to one of: [auto, perspective, brown, fisheye, spherical]. Default: '
'%(default)s'))
parser.add_argument('--max-concurrency',
metavar='<positive integer>',
@ -577,8 +587,9 @@ def config():
parser.add_argument('--force-gps',
action='store_true',
default=False,
help=('Use images\' gps exif data for reconstruction, even if there are gcps present.'
'This flag is useful if you have high precision gps measurements.'))
help=('Use images\' GPS exif data for reconstruction, even if there are GCPs present.'
'This flag is useful if you have high precision GPS measurements. '
'If there are no GCPs, this flag does nothing. Default: %(default)s'))
args = parser.parse_args()

Wyświetl plik

@ -52,13 +52,32 @@ def create_dem(input_point_cloud, dem_type, output_type='max', radiuses=['0.56']
ext_width = extent['maxx'] - extent['minx']
ext_height = extent['maxy'] - extent['miny']
final_dem_resolution = (int(math.ceil(ext_width / float(resolution))),
int(math.ceil(ext_height / float(resolution))))
final_dem_pixels = final_dem_resolution[0] * final_dem_resolution[1]
w, h = (int(math.ceil(ext_width / float(resolution))),
int(math.ceil(ext_height / float(resolution))))
# Set a floor, no matter the resolution parameter
# (sometimes a wrongly estimated scale of the model can cause the resolution
# to be set unrealistically low, causing errors)
RES_FLOOR = 64
if w < RES_FLOOR and h < RES_FLOOR:
prev_w, prev_h = w, h
if w >= h:
w, h = (RES_FLOOR, int(math.ceil(ext_height / ext_width * RES_FLOOR)))
else:
w, h = (int(math.ceil(ext_width / ext_height * RES_FLOOR)), RES_FLOOR)
floor_ratio = prev_w / float(w)
resolution *= floor_ratio
radiuses = [str(float(r) * floor_ratio) for r in radiuses]
log.ODM_WARNING("Really low resolution DEM requested %s will set floor at %s pixels. Resolution changed to %s. The scale of this reconstruction might be off." % ((prev_w, prev_h), RES_FLOOR, resolution))
final_dem_pixels = w * h
num_splits = int(max(1, math.ceil(math.log(math.ceil(final_dem_pixels / float(max_tile_size * max_tile_size)))/math.log(2))))
num_tiles = num_splits * num_splits
log.ODM_INFO("DEM resolution is %s, max tile size is %s, will split DEM generation into %s tiles" % (final_dem_resolution, max_tile_size, num_tiles))
log.ODM_INFO("DEM resolution is %s, max tile size is %s, will split DEM generation into %s tiles" % ((h, w), max_tile_size, num_tiles))
tile_bounds_width = ext_width / float(num_splits)
tile_bounds_height = ext_height / float(num_splits)

Wyświetl plik

@ -20,14 +20,6 @@ class OSFMContext:
system.run('%s/bin/opensfm %s "%s"' %
(context.opensfm_path, command, self.opensfm_project_path))
def export_bundler(self, destination_bundle_file, rerun=False):
if not io.file_exists(destination_bundle_file) or rerun:
# convert back to bundler's format
system.run('%s/bin/export_bundler "%s"' %
(context.opensfm_path, self.opensfm_project_path))
else:
log.ODM_WARNING('Found a valid Bundler file in: %s' % destination_bundle_file)
def is_reconstruction_done(self):
tracks_file = os.path.join(self.opensfm_project_path, 'tracks.csv')
reconstruction_file = os.path.join(self.opensfm_project_path, 'reconstruction.json')
@ -74,10 +66,13 @@ class OSFMContext:
# create file list
has_alt = True
has_gps = False
with open(list_path, 'w') as fout:
for photo in photos:
if not photo.altitude:
has_alt = False
if photo.latitude is not None and photo.longitude is not None:
has_gps = True
fout.write('%s\n' % io.join_paths(images_path, photo.filename))
# check for image_groups.txt (split-merge)
@ -111,26 +106,33 @@ class OSFMContext:
"optimize_camera_parameters: %s" % ('no' if args.use_fixed_camera_params or args.cameras else 'yes'),
"undistorted_image_format: png", # mvs-texturing exhibits artifacts with JPG
"bundle_outlier_filtering_type: AUTO",
"align_orientation_prior: vertical",
]
# TODO: add BOW matching when dataset is not georeferenced (no gps)
if args.camera_lens != 'auto':
config.append("camera_projection_type: %s" % args.camera_lens.upper())
if not has_gps:
log.ODM_INFO("No GPS information, using BOW matching")
config.append("matcher_type: WORDS")
if has_alt:
log.ODM_INFO("Altitude data detected, enabling it for GPS alignment")
config.append("use_altitude_tag: yes")
if has_alt or gcp_path:
config.append("align_method: naive")
config.append("align_method: auto")
else:
config.append("align_method: orientation_prior")
config.append("align_orientation_prior: vertical")
if args.use_hybrid_bundle_adjustment:
log.ODM_INFO("Enabling hybrid bundle adjustment")
config.append("bundle_interval: 100") # Bundle after adding 'bundle_interval' cameras
config.append("bundle_new_points_ratio: 1.2") # Bundle when (new points) / (bundled points) > bundle_new_points_ratio
config.append("local_bundle_radius: 1") # Max image graph distance for images to be included in local bundle adjustment
else:
config.append("local_bundle_radius: 0")
if gcp_path:
config.append("bundle_use_gcp: yes")
if not args.force_gps:
@ -225,30 +227,6 @@ class OSFMContext:
else:
log.ODM_WARNING("Tried to update configuration, but %s does not exist." % cfg_file)
def save_absolute_image_list_to(self, file):
"""
Writes a copy of the image_list.txt file and makes sure that all paths
written in it are absolute paths and not relative paths.
"""
image_list_file = self.path("image_list.txt")
if io.file_exists(image_list_file):
with open(image_list_file, 'r') as f:
content = f.read()
lines = []
for line in map(str.strip, content.split('\n')):
if line and not line.startswith("/"):
line = os.path.abspath(os.path.join(self.opensfm_project_path, line))
lines.append(line)
with open(file, 'w') as f:
f.write("\n".join(lines))
log.ODM_INFO("Wrote %s with absolute paths" % file)
else:
log.ODM_WARNING("No %s found, cannot create %s" % (image_list_file, file))
def name(self):
return os.path.basename(os.path.abspath(self.path("..")))

Wyświetl plik

@ -259,9 +259,6 @@ class ODM_Tree(object):
# mve
self.mve_model = io.join_paths(self.mve, 'mve_dense_point_cloud.ply')
self.mve_path = io.join_paths(self.opensfm, 'mve')
self.mve_image_list = io.join_paths(self.mve_path, 'list.txt')
self.mve_bundle = io.join_paths(self.mve_path, 'bundle/bundle.out')
self.mve_views = io.join_paths(self.mve, 'views')
# filter points

Wyświetl plik

@ -50,9 +50,7 @@ RUN apt-get install --no-install-recommends -y \
python-dev \
python-gdal \
python-matplotlib \
python-networkx \
python-pip \
python-pyproj \
python-software-properties \
python-wheel \
swig2.0 \
@ -67,18 +65,20 @@ RUN pip install -U \
exifread \
gpxpy \
loky \
numpy==1.15.4 \
psutil \
pyproj \
PyYAML==3.13 \
repoze.lru \
scipy==1.2.1 \
shapely \
xmltodict \
rasterio \
attrs==19.1.0 \
pyodm==1.5.2b1 \
Pillow
Pillow \
networkx \
scipy==1.2.1 \
numpy==1.15.4 \
shapely \
pyproj \
psutil \
joblib
RUN pip install --upgrade cryptography && python -m easy_install --upgrade pyOpenSSL

1
run.py
Wyświetl plik

@ -49,6 +49,7 @@ if __name__ == '__main__':
quote(os.path.join(args.project_path, "odm_25dmeshing")),
quote(os.path.join(args.project_path, "odm_25dtexturing")),
quote(os.path.join(args.project_path, "mve")),
quote(os.path.join(args.project_path, "entwine_pointcloud")),
quote(os.path.join(args.project_path, "submodels")),
]))

Wyświetl plik

@ -21,19 +21,6 @@ class ODMMveStage(types.ODM_Stage):
# check if reconstruction was done before
if not io.file_exists(tree.mve_model) or self.rerun():
# cleanup if a rerun
if io.dir_exists(tree.mve_path) and self.rerun():
shutil.rmtree(tree.mve_path)
# make bundle directory
if not io.file_exists(tree.mve_bundle):
system.mkdir_p(tree.mve_path)
system.mkdir_p(io.join_paths(tree.mve_path, 'bundle'))
octx = OSFMContext(tree.opensfm)
octx.save_absolute_image_list_to(tree.mve_image_list)
io.copy(tree.opensfm_bundle, tree.mve_bundle)
# mve makescene wants the output directory
# to not exists before executing it (otherwise it
# will prompt the user for confirmation)
@ -42,22 +29,16 @@ class ODMMveStage(types.ODM_Stage):
# run mve makescene
if not io.dir_exists(tree.mve_views):
system.run('%s "%s" "%s"' % (context.makescene_path, tree.mve_path, tree.mve), env_vars={'OMP_NUM_THREADS': args.max_concurrency})
system.run('%s "%s" "%s"' % (context.makescene_path, tree.opensfm_reconstruction_nvm, tree.mve), env_vars={'OMP_NUM_THREADS': args.max_concurrency})
self.update_progress(10)
# Compute mve output scale based on depthmap_resolution
max_width = 0
max_height = 0
for photo in photos:
max_width = max(photo.width, max_width)
max_height = max(photo.height, max_height)
max_pixels = args.depthmap_resolution * args.depthmap_resolution
if max_width * max_height <= max_pixels:
if outputs['undist_image_max_size'] * outputs['undist_image_max_size'] <= max_pixels:
mve_output_scale = 0
else:
ratio = float(max_width * max_height) / float(max_pixels)
ratio = float(outputs['undist_image_max_size'] * outputs['undist_image_max_size']) / float(max_pixels)
mve_output_scale = int(math.ceil(math.log(ratio) / math.log(4.0)))
dmrecon_config = [

Wyświetl plik

@ -33,9 +33,6 @@ class ODMOrthoPhotoStage(types.ODM_Stage):
'verbose': verbose
}
# Have geo coordinates?
georef = reconstruction.georef
# Check if the georef object is initialized
# (during a --rerun this might not be)
# TODO: this should be moved to a more central location?

Wyświetl plik

@ -44,11 +44,19 @@ class ODMOpenSfMStage(types.ODM_Stage):
output_file = tree.opensfm_reconstruction
updated_config_flag_file = octx.path('updated_config.txt')
# 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),
args.depthmap_resolution
)
if not io.file_exists(updated_config_flag_file) or self.rerun():
octx.update_config({'undistorted_image_max_size': gsd.image_max_size(photos, args.orthophoto_resolution, tree.opensfm_reconstruction, ignore_gsd=args.ignore_gsd)})
octx.update_config({'undistorted_image_max_size': outputs['undist_image_max_size']})
octx.touch(updated_config_flag_file)
# These will be used for texturing
# These will be used for texturing / MVS
undistorted_images_path = octx.path("undistorted")
if not io.dir_exists(undistorted_images_path) or self.rerun():
@ -59,7 +67,7 @@ class ODMOpenSfMStage(types.ODM_Stage):
self.update_progress(80)
if not io.file_exists(tree.opensfm_reconstruction_nvm) or self.rerun():
octx.run('export_visualsfm --undistorted')
octx.run('export_visualsfm --undistorted --points')
else:
log.ODM_WARNING('Found a valid OpenSfM NVM reconstruction file in: %s' %
tree.opensfm_reconstruction_nvm)
@ -80,9 +88,6 @@ class ODMOpenSfMStage(types.ODM_Stage):
else:
log.ODM_WARNING("Found a valid dense reconstruction in %s" % output_file)
# check if reconstruction was exported to bundler before
octx.export_bundler(tree.opensfm_bundle_list, self.rerun())
self.update_progress(90)
if reconstruction.is_georeferenced() and (not io.file_exists(tree.opensfm_transformation) or self.rerun()):