kopia lustrzana https://github.com/OpenDroneMap/ODM
rodzic
1ad82173e3
commit
1aa2904267
|
|
@ -21,7 +21,7 @@ libexiv2-dev liblas-bin python-matplotlib libatlas-base-dev swig2.0 python-wheel
|
|||
RUN apt-get remove libdc1394-22-dev
|
||||
RUN pip install --upgrade pip
|
||||
RUN pip install setuptools
|
||||
RUN pip install -U PyYAML exifread gpxpy xmltodict catkin-pkg appsettings https://github.com/OpenDroneMap/gippy/archive/v0.3.9.tar.gz loky
|
||||
RUN pip install -U PyYAML exifread gpxpy xmltodict catkin-pkg appsettings https://github.com/OpenDroneMap/gippy/archive/v0.3.9.tar.gz loky scipy shapely numpy pyproj
|
||||
|
||||
ENV PYTHONPATH="$PYTHONPATH:/code/SuperBuild/install/lib/python2.7/dist-packages"
|
||||
ENV PYTHONPATH="$PYTHONPATH:/code/SuperBuild/src/opensfm"
|
||||
|
|
|
|||
|
|
@ -97,6 +97,9 @@ install() {
|
|||
python-wheel \
|
||||
libboost-log-dev
|
||||
|
||||
echo "Installing split-merge Dependencies"
|
||||
pip install -U scipy shapely numpy pyproj
|
||||
|
||||
pip install -U https://github.com/OpenDroneMap/gippy/archive/v0.3.9.tar.gz
|
||||
|
||||
echo "Compiling SuperBuild"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ libexiv2-dev liblas-bin python-matplotlib libatlas-base-dev swig2.0 python-wheel
|
|||
RUN apt-get remove libdc1394-22-dev
|
||||
RUN pip install --upgrade pip
|
||||
RUN pip install setuptools
|
||||
RUN pip install -U PyYAML exifread gpxpy xmltodict catkin-pkg appsettings https://github.com/OpenDroneMap/gippy/archive/v0.3.9.tar.gz loky
|
||||
RUN pip install -U PyYAML exifread gpxpy xmltodict catkin-pkg appsettings https://github.com/OpenDroneMap/gippy/archive/v0.3.9.tar.gz loky scipy shapely numpy pyproj
|
||||
|
||||
ENV PYTHONPATH="$PYTHONPATH:/code/SuperBuild/install/lib/python2.7/dist-packages"
|
||||
ENV PYTHONPATH="$PYTHONPATH:/code/SuperBuild/src/opensfm"
|
||||
|
|
|
|||
|
|
@ -448,12 +448,13 @@ class ODM_Tree(object):
|
|||
self.odm_georeferencing_latlon = io.join_paths(
|
||||
self.odm_georeferencing, 'latlon.txt')
|
||||
self.odm_georeferencing_coords = io.join_paths(
|
||||
self.root_path, 'coords.txt') # Todo put this somewhere better
|
||||
self.odm_georeferencing, 'coords.txt')
|
||||
self.odm_georeferencing_gcp = gcp_file or io.find('gcp_list.txt', self.root_path)
|
||||
self.odm_georeferencing_utm_log = io.join_paths(
|
||||
self.odm_georeferencing, 'odm_georeferencing_utm_log.txt')
|
||||
self.odm_georeferencing_log = 'odm_georeferencing_log.txt'
|
||||
self.odm_georeferencing_transform_file = 'odm_georeferencing_transform.txt'
|
||||
self.odm_georeferencing_proj = 'proj.txt'
|
||||
self.odm_georeferencing_model_txt_geo = 'odm_georeferencing_model_geo.txt'
|
||||
self.odm_georeferencing_model_ply_geo = 'odm_georeferenced_model.ply'
|
||||
self.odm_georeferencing_model_obj_geo = 'odm_textured_model_geo.obj'
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@ class ODMLoadDatasetCell(ecto.Cell):
|
|||
system.mkdir_p(images_dir)
|
||||
copied = [copyfile(io.join_paths(input_dir, f), io.join_paths(images_dir, f)) for f in get_images(input_dir)]
|
||||
|
||||
# define paths and create working directories
|
||||
system.mkdir_p(tree.odm_georeferencing)
|
||||
if args.use_25dmesh: system.mkdir_p(tree.odm_25dgeoreferencing)
|
||||
|
||||
log.ODM_DEBUG('Loading dataset from: %s' % images_dir)
|
||||
|
||||
files = get_images(images_dir)
|
||||
|
|
@ -107,5 +111,9 @@ class ODMLoadDatasetCell(ecto.Cell):
|
|||
else:
|
||||
outputs.reconstruction = types.ODM_Reconstruction(photos, projstring=self.params.proj)
|
||||
|
||||
# Save proj to file for future use
|
||||
with open(io.join_paths(tree.odm_georeferencing, tree.odm_georeferencing_proj), 'w') as f:
|
||||
f.write(outputs.reconstruction.projection.srs)
|
||||
|
||||
log.ODM_INFO('Running ODM Load Dataset Cell - Finished')
|
||||
return ecto.OK if args.end_with != 'dataset' else ecto.QUIT
|
||||
|
|
|
|||
|
|
@ -2,78 +2,15 @@ from opendm import io
|
|||
from opendm import log
|
||||
from opendm import system
|
||||
import argparse
|
||||
from osgeo import ogr
|
||||
from functools import partial
|
||||
import os
|
||||
from opensfm.large import metadataset
|
||||
|
||||
|
||||
def create_bounds_file(clusters_geojson_path):
|
||||
# Create a convex hull around the boundary
|
||||
# as to encompass the entire area (no holes)
|
||||
driver = ogr.GetDriverByName('GeoJSON')
|
||||
ds = driver.Open(clusters_geojson_path, 0) # read-only
|
||||
in_layer = ds.GetLayer()
|
||||
|
||||
|
||||
# Save to a new file
|
||||
out_path = io.extract_path_from_file(clusters_geojson_path)
|
||||
bounds_geojson_path = os.path.join(out_path, 'bounds.geojson')
|
||||
if os.path.exists(bounds_geojson_path):
|
||||
driver.DeleteDataSource(bounds_geojson_path)
|
||||
|
||||
out_ds = driver.CreateDataSource(bounds_geojson_path)
|
||||
out_layer = out_ds.CreateLayer("bounds.geojson", geom_type=ogr.wkbPolygon)
|
||||
out_layer.CreateField(ogr.FieldDefn('ID', ogr.OFTInteger))
|
||||
|
||||
layer_def = in_layer.GetLayerDefn()
|
||||
|
||||
feature_def = in_layer.GetLayerDefn()
|
||||
|
||||
# For each submodel, create a convex hull
|
||||
num_clusters = 0
|
||||
# get number of submodels
|
||||
for in_feat in in_layer:
|
||||
x = in_feat.GetFieldAsInteger('submodel')
|
||||
if x > num_clusters:
|
||||
num_clusters = x
|
||||
num_clusters += 1
|
||||
log.ODM_DEBUG("Number of clusters: {}".format(num_clusters))
|
||||
|
||||
in_layer.ResetReading()
|
||||
|
||||
hull_collection = ogr.Geometry(ogr.wkbGeometryCollection)
|
||||
for i in range(num_clusters):
|
||||
|
||||
# Collect all Geometry
|
||||
geomcol = ogr.Geometry(ogr.wkbGeometryCollection)
|
||||
for in_feat in in_layer:
|
||||
if in_feat.GetFieldAsInteger('submodel') == i:
|
||||
# add point to geometry feature
|
||||
geomcol.AddGeometry(in_feat.GetGeometryRef())
|
||||
in_layer.ResetReading()
|
||||
|
||||
# Calculate convex hull for each feature
|
||||
convexhull = geomcol.ConvexHull()
|
||||
hull_collection.AddGeometry(convexhull)
|
||||
|
||||
## geomcol.Destroy()
|
||||
|
||||
feat_iter = 0
|
||||
|
||||
for feat in hull_collection:
|
||||
|
||||
out_feat = ogr.Feature(feature_def)
|
||||
out_feat.SetGeometry(feat)
|
||||
# add ID
|
||||
out_feat.SetField(0, feat_iter)
|
||||
feat_iter += 1
|
||||
out_layer.CreateFeature(out_feat)
|
||||
out_feat = None
|
||||
|
||||
# Save and close data sources
|
||||
out_ds = ds = None
|
||||
|
||||
return bounds_geojson_path
|
||||
from scipy.spatial import Voronoi
|
||||
from shapely.geometry import shape, LineString, Point
|
||||
import shapely.ops
|
||||
import numpy as np
|
||||
import json
|
||||
import pyproj
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
@ -87,54 +24,129 @@ if __name__ == "__main__":
|
|||
args = parser.parse_args()
|
||||
|
||||
submodels_path = io.join_paths(args.dataset, 'submodels')
|
||||
sfm_path = io.join_paths(args.dataset, 'opensfm')
|
||||
meta_data = metadataset.MetaDataSet(sfm_path)
|
||||
data = metadataset.DataSet(sfm_path)
|
||||
voronoi_file = io.join_paths(meta_data.data_path, 'voronoi.geojson')
|
||||
proj_path = io.join_paths(args.dataset, "odm_georeferencing/proj.txt")
|
||||
out_tif = io.join_paths(args.dataset, "merged.tif")
|
||||
addo_log = io.join_paths(args.dataset, "gdal_addo.log")
|
||||
|
||||
path = os.path.join(args.dataset, 'opensfm')
|
||||
meta_data = metadataset.MetaDataSet(path)
|
||||
data = metadataset.DataSet(path)
|
||||
bounds_file = None
|
||||
bounds_files = {}
|
||||
for folder in os.listdir(io.join_paths(args.dataset, 'submodels')):
|
||||
if 'submodel' in folder:
|
||||
folder_number = '0' if folder.split('_')[1] == '0000' else folder.split('_')[1].lstrip('0')
|
||||
bounds_file = io.join_paths(submodels_path, folder +
|
||||
"/odm_georeferencing/odm_georeferenced_model.bounds.geojson")
|
||||
if io.file_exists(bounds_file):
|
||||
bounds_files[folder_number] = bounds_file
|
||||
|
||||
clusters_file = os.path.join(args.dataset, "submodels/opensfm/clusters_with_neighbors.geojson")
|
||||
if io.file_exists(clusters_file):
|
||||
log.ODM_DEBUG("Creating cluster bounds")
|
||||
bounds_file = create_bounds_file(clusters_file)
|
||||
else:
|
||||
log.ODM_ERROR("Clusters file not found")
|
||||
exit()
|
||||
# Do voronoi calcs
|
||||
# # load clusters
|
||||
images, positions, labels, centers = meta_data.load_clusters()
|
||||
cluster_proj = pyproj.Proj(init='epsg:4326')
|
||||
with open(proj_path, 'r') as fr:
|
||||
transform_proj = pyproj.Proj(fr.read())
|
||||
|
||||
if not io.file_exists(bounds_file):
|
||||
log.ODM_ERROR("Bounds file not created. Exiting...")
|
||||
else:
|
||||
# List of tifs paths to merge
|
||||
ortho_tifs = {}
|
||||
for folder in os.listdir(io.join_paths(args.dataset, 'submodels')):
|
||||
if 'submodel' in folder:
|
||||
folder_number = folder.split('_')[1] # string extract number
|
||||
tif_file = io.join_paths(submodels_path, folder + "/odm_orthophoto/odm_orthophoto.tif")
|
||||
if io.file_exists(tif_file):
|
||||
ortho_tifs[folder_number] = tif_file
|
||||
# projection transformation
|
||||
project = partial(
|
||||
pyproj.transform,
|
||||
cluster_proj,
|
||||
transform_proj)
|
||||
|
||||
kwargs = {
|
||||
'f_out': io.join_paths(submodels_path, 'big-ole-tiff.tif'),
|
||||
# turn this into a list of points
|
||||
pos_transformed = [shapely.ops.transform(project, Point(x[1], x[0])) for x in positions]
|
||||
|
||||
#back to ndarray
|
||||
positions = np.array([pos_transformed[x].coords[0] for x in range(len(pos_transformed))])
|
||||
|
||||
clust = np.concatenate((images, labels, positions), 1)
|
||||
# Run voronoi on the whole cluster
|
||||
vor = Voronoi(clust[:, [2, 3]].astype(float))
|
||||
lines = [
|
||||
LineString(vor.vertices[line])
|
||||
for line in vor.ridge_vertices
|
||||
if -1 not in line
|
||||
]
|
||||
# # For each part, build a boundary polygon
|
||||
v_poly_dis_intersected = {}
|
||||
for subnum in np.unique(clust[:, 1]):
|
||||
submodel = clust[clust[:, 1] == subnum]
|
||||
polygons = []
|
||||
for poly in shapely.ops.polygonize(lines):
|
||||
for point in submodel:
|
||||
if poly.contains(Point(point[[2, 3]])):
|
||||
polygons.append(poly) # Todo: this is expensive
|
||||
break
|
||||
# Dissolve list of polyogns
|
||||
voronoi_polygons_dissolved = shapely.ops.unary_union(polygons)
|
||||
# intersect with bounds
|
||||
with open(bounds_files[subnum]) as f:
|
||||
# There should only be one polygon here
|
||||
bounds = shape(json.loads(f.read())['features'][0]['geometry'])
|
||||
|
||||
v_poly_dis_intersected[subnum] = voronoi_polygons_dissolved.intersection(bounds)
|
||||
|
||||
features = []
|
||||
for submodel in v_poly_dis_intersected:
|
||||
features.append({
|
||||
"type": "Feature",
|
||||
"geometry": shapely.geometry.mapping(v_poly_dis_intersected[submodel]),
|
||||
"properties": {
|
||||
"submodel": int(submodel)
|
||||
}
|
||||
})
|
||||
|
||||
polygons_layer = {
|
||||
"type": "FeatureCollection",
|
||||
"features": features,
|
||||
"crs": {"type": "name", "properties": {"name": transform_proj.srs, "type": "proj4"}}
|
||||
}
|
||||
|
||||
with open(voronoi_file, "w") as f:
|
||||
json.dump(polygons_layer, f)
|
||||
|
||||
ortho_tifs = {}
|
||||
for folder in os.listdir(io.join_paths(args.dataset, 'submodels')):
|
||||
if 'submodel' in folder:
|
||||
folder_number = folder.split('_')[1] # string extract number
|
||||
tif_file = io.join_paths(submodels_path, folder + "/odm_orthophoto/odm_orthophoto.tif")
|
||||
if io.file_exists(tif_file):
|
||||
ortho_tifs[folder_number] = tif_file
|
||||
|
||||
kwargs = {
|
||||
'f_out': out_tif,
|
||||
'files': ' '.join(ortho_tifs.values()),
|
||||
'clusters': bounds_file
|
||||
'clusters': voronoi_file
|
||||
}
|
||||
|
||||
if io.file_exists(kwargs['f_out']) and not args.overwrite:
|
||||
log.ODM_ERROR("File {f_out} exists, use --overwrite to force overwrite of file.".format(**kwargs))
|
||||
else:
|
||||
# use bounds as cutlines (blending)
|
||||
system.run('gdal_merge.py -o {f_out} '
|
||||
'-createonly '
|
||||
'-co "BIGTIFF=YES" '
|
||||
'-co "BLOCKXSIZE=512" '
|
||||
'-co "BLOCKYSIZE=512" {files}'.format(**kwargs)
|
||||
)
|
||||
if io.file_exists(kwargs['f_out']) and not args.overwrite:
|
||||
log.ODM_ERROR("File {f_out} exists, use --overwrite to force overwrite of file.".format(**kwargs))
|
||||
else:
|
||||
# use bounds as cutlines (blending)
|
||||
system.run('gdal_merge.py -o {f_out} '
|
||||
'-createonly '
|
||||
'-co "BIGTIFF=YES" '
|
||||
'-co "BLOCKXSIZE=512" '
|
||||
'-co "BLOCKYSIZE=512" {files}'.format(**kwargs)
|
||||
)
|
||||
|
||||
for tif in ortho_tifs:
|
||||
kwargs['name'] = '0' if tif == '0000' else tif.lstrip('0') # is tif a tuple?
|
||||
kwargs['file'] = ortho_tifs[tif]
|
||||
system.run('gdalwarp -cutline {clusters} '
|
||||
'-cwhere "NAME = \'{name}\'" '
|
||||
'-r lanczos -multi -wo NUM_THREADS=ALL_CPUS '
|
||||
'{file} {f_out}'.format(**kwargs)
|
||||
)
|
||||
for tif in ortho_tifs:
|
||||
kwargs['name'] = '0' if tif == '0000' else tif.lstrip('0') # is tif a tuple?
|
||||
kwargs['file'] = ortho_tifs[tif]
|
||||
system.run('gdalwarp -cutline {clusters} '
|
||||
'-cwhere "submodel = \'{name}\'" '
|
||||
'-r lanczos -multi -wo NUM_THREADS=ALL_CPUS '
|
||||
' {file} {f_out}'.format(**kwargs)
|
||||
)
|
||||
|
||||
log.ODM_INFO("Building Overviews")
|
||||
kwargs = {
|
||||
'orthophoto': out_tif,
|
||||
'log': addo_log
|
||||
}
|
||||
# Run gdaladdo
|
||||
system.run('gdaladdo -ro -r average '
|
||||
'--config BIGTIFF_OVERVIEW IF_SAFER '
|
||||
'--config COMPRESS_OVERVIEW JPEG '
|
||||
'{orthophoto} 2 4 8 16 > {log}'.format(**kwargs))
|
||||
|
|
@ -14,3 +14,4 @@ python $DIR/split.py $1
|
|||
python $DIR/run_reconstructions.py $1
|
||||
python $DIR/align.py $1
|
||||
python $DIR/run_dense.py $1
|
||||
python $DIR/merge.py $1
|
||||
|
|
|
|||
|
|
@ -40,10 +40,6 @@ class ODMGeoreferencingCell(ecto.Cell):
|
|||
doPointCloudGeo = True
|
||||
verbose = '-verbose' if self.params.verbose else ''
|
||||
|
||||
# define paths and create working directories
|
||||
system.mkdir_p(tree.odm_georeferencing)
|
||||
if args.use_25dmesh: system.mkdir_p(tree.odm_25dgeoreferencing)
|
||||
|
||||
# check if we rerun cell or not
|
||||
rerun_cell = (args.rerun is not None and
|
||||
args.rerun == 'odm_georeferencing') or \
|
||||
|
|
|
|||
|
|
@ -76,7 +76,9 @@ class ODMOpenSfMCell(ecto.Cell):
|
|||
"feature_min_frames: %s" % self.params.feature_min_frames,
|
||||
"processes: %s" % self.params.processes,
|
||||
"matching_gps_neighbors: %s" % self.params.matching_gps_neighbors,
|
||||
"depthmap_resolution: 640",
|
||||
# "depthmap_resolution: 2560",
|
||||
# "depthmap_min_patch_sd: 4.0",
|
||||
# "depthmap_min_consistent_views: 3",
|
||||
"optimize_camera_parameters: %s" % ('no' if self.params.fixed_camera_params else 'yes')
|
||||
]
|
||||
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue