kopia lustrzana https://github.com/OpenDroneMap/ODM
152 wiersze
5.7 KiB
Python
152 wiersze
5.7 KiB
Python
from opendm import io
|
|
from opendm import log
|
|
from opendm import system
|
|
import argparse
|
|
from functools import partial
|
|
import os
|
|
from opensfm.large import metadataset
|
|
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__":
|
|
parser = argparse.ArgumentParser(description='Align metadaset submodels')
|
|
parser.add_argument('dataset',
|
|
help='path to the dataset to be processed')
|
|
parser.add_argument('--overwrite', '-o',
|
|
action='store_true',
|
|
default=False,
|
|
help='Force overwrite of generated files')
|
|
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")
|
|
|
|
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
|
|
|
|
# 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())
|
|
|
|
# projection transformation
|
|
project = partial(
|
|
pyproj.transform,
|
|
cluster_proj,
|
|
transform_proj)
|
|
|
|
# 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': 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)
|
|
)
|
|
|
|
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)) |