kopia lustrzana https://github.com/OpenDroneMap/ODM
Add quickpreview module, fix cutline polygons edge-case
rodzic
1ed9087e4f
commit
0589483b9b
|
@ -0,0 +1,29 @@
|
||||||
|
# Merge Preview
|
||||||
|
|
||||||
|
Quickly projects drone images on a map by using georeferencing, camera angles and a global DTM. The images are then merged using ODM's split-merge algorithms.
|
||||||
|
|
||||||
|
Quality is obviously not good, works only for nadir-only images and requires the images to have gimbal/camera angle information (not all drones provide this information).
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Install DDB (required for geoprojection)
|
||||||
|
|
||||||
|
curl -fsSL https://get.dronedb.app -o get-ddb.sh
|
||||||
|
sh get-ddb.sh
|
||||||
|
|
||||||
|
# Run
|
||||||
|
|
||||||
|
python3 mergepreview.py -i images/*.JPG --size 25%
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
![screen](https://user-images.githubusercontent.com/1951843/134249725-e178489a-e271-4244-abed-e624cd510b88.png)
|
||||||
|
|
||||||
|
|
||||||
|
[Sheffield Park](https://community.opendronemap.org/t/sheffield-park-1/58) images processed with this script.
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
|
||||||
|
This script is highly experimental. We welcome contributions to improve it.
|
|
@ -0,0 +1,126 @@
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
sys.path.append("../../")
|
||||||
|
|
||||||
|
import os
|
||||||
|
from opendm import orthophoto
|
||||||
|
from opendm.cutline import compute_cutline
|
||||||
|
import glob
|
||||||
|
from opendm.system import run
|
||||||
|
from opendm import log
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Quick Merge Preview')
|
||||||
|
parser.add_argument('input',
|
||||||
|
metavar='<paths>',
|
||||||
|
nargs='+',
|
||||||
|
help='Path to input images or image folder')
|
||||||
|
parser.add_argument('--size', '-s',
|
||||||
|
metavar='<percentage>',
|
||||||
|
type=str,
|
||||||
|
help='Size in percentage terms',
|
||||||
|
default='25%')
|
||||||
|
parser.add_argument('--force', '-f',
|
||||||
|
action='store_true',
|
||||||
|
default=False,
|
||||||
|
help="Force remove existing directories")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
try:
|
||||||
|
log.ODM_INFO("Checking for DDB...")
|
||||||
|
run("ddb --version")
|
||||||
|
except:
|
||||||
|
log.ODM_ERROR("ddb is not installed. Install it first: https://docs.dronedb.app")
|
||||||
|
|
||||||
|
if len(args.input) == 1 and os.path.isdir(args.input[0]):
|
||||||
|
input_images = []
|
||||||
|
for ext in ["JPG", "JPEG", "TIF", "tiff", "tif", "TIFF"]:
|
||||||
|
input_images += glob.glob(os.path.join(args.input[0], "*.%s" % ext))
|
||||||
|
else:
|
||||||
|
input_images = args.input
|
||||||
|
|
||||||
|
log.ODM_INFO("Processing %s images" % len(input_images))
|
||||||
|
|
||||||
|
if len(input_images) == 0:
|
||||||
|
log.ODM_ERROR("No images")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
cwd_path = os.path.dirname(input_images[0])
|
||||||
|
tmp_path = os.path.join(cwd_path, "tmp")
|
||||||
|
if os.path.isdir(tmp_path):
|
||||||
|
if args.force:
|
||||||
|
log.ODM_INFO("Removing previous directory %s" % tmp_path)
|
||||||
|
shutil.rmtree(tmp_path)
|
||||||
|
else:
|
||||||
|
log.ODM_ERROR("%s exists. Pass --force to override." % tmp_path)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
os.makedirs(tmp_path)
|
||||||
|
|
||||||
|
for f in input_images:
|
||||||
|
name, _ = os.path.splitext(os.path.basename(f))
|
||||||
|
geojson = os.path.join(tmp_path, "%s.geojson" % name)
|
||||||
|
gpkg = os.path.join(tmp_path, "%s.gpkg" % name)
|
||||||
|
|
||||||
|
run("ddb geoproj \"%s\" \"%s\" -s \"%s\"" % (tmp_path, f, args.size))
|
||||||
|
|
||||||
|
# Bounds (GPKG)
|
||||||
|
run("ddb info --format geojson --geometry polygon \"%s\" > \"%s\"" % (f, geojson))
|
||||||
|
run("ogr2ogr \"%s\" \"%s\"" % (gpkg, geojson))
|
||||||
|
|
||||||
|
log.ODM_INFO("Computing cutlines")
|
||||||
|
|
||||||
|
projected_images = glob.glob(os.path.join(tmp_path, "*.tif"))
|
||||||
|
all_orthos_and_ortho_cuts = []
|
||||||
|
|
||||||
|
for f in projected_images:
|
||||||
|
name, _ = os.path.splitext(os.path.basename(f))
|
||||||
|
cutline_file = os.path.join(tmp_path, "%s_cutline.gpkg" % name)
|
||||||
|
bounds_file_path = os.path.join(tmp_path, "%s.gpkg" % name)
|
||||||
|
|
||||||
|
compute_cutline(f,
|
||||||
|
bounds_file_path,
|
||||||
|
cutline_file,
|
||||||
|
4,
|
||||||
|
scale=1)
|
||||||
|
|
||||||
|
cut_raster = os.path.join(tmp_path, "%s_cut.tif" % name)
|
||||||
|
orthophoto.compute_mask_raster(f, cutline_file,
|
||||||
|
cut_raster,
|
||||||
|
blend_distance=20, only_max_coords_feature=True)
|
||||||
|
|
||||||
|
feathered_raster = os.path.join(tmp_path, "%s_feathered.tif" % name)
|
||||||
|
|
||||||
|
orthophoto.feather_raster(f, feathered_raster,
|
||||||
|
blend_distance=20
|
||||||
|
)
|
||||||
|
|
||||||
|
all_orthos_and_ortho_cuts.append([feathered_raster, cut_raster])
|
||||||
|
|
||||||
|
log.ODM_INFO("Merging...")
|
||||||
|
|
||||||
|
if len(all_orthos_and_ortho_cuts) > 1:
|
||||||
|
# TODO: histogram matching via rasterio
|
||||||
|
# currently parts have different color tones
|
||||||
|
output_file = os.path.join(cwd_path, 'mergepreview.tif')
|
||||||
|
|
||||||
|
if os.path.isfile(output_file):
|
||||||
|
os.remove(output_file)
|
||||||
|
|
||||||
|
orthophoto.merge(all_orthos_and_ortho_cuts, output_file, {
|
||||||
|
'TILED': 'YES',
|
||||||
|
'COMPRESS': 'LZW',
|
||||||
|
'PREDICTOR': '2',
|
||||||
|
'BIGTIFF': 'IF_SAFER',
|
||||||
|
'BLOCKXSIZE': 512,
|
||||||
|
'BLOCKYSIZE': 512
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
log.ODM_INFO("Wrote %s" % output_file)
|
||||||
|
shutil.rmtree(tmp_path)
|
||||||
|
else:
|
||||||
|
log.ODM_ERROR("Error: no orthos found to merge")
|
||||||
|
exit(1)
|
|
@ -145,9 +145,12 @@ def compute_cutline(orthophoto_file, crop_area_file, destination, max_concurrenc
|
||||||
if len(polygons) == 0:
|
if len(polygons) == 0:
|
||||||
log.ODM_WARNING("No polygons, cannot compute cutline")
|
log.ODM_WARNING("No polygons, cannot compute cutline")
|
||||||
return
|
return
|
||||||
|
|
||||||
log.ODM_INFO("Merging polygons")
|
log.ODM_INFO("Merging polygons")
|
||||||
cutline_polygons = unary_union(polygons)
|
cutline_polygons = unary_union(polygons)
|
||||||
|
if not hasattr(cutline_polygons, '__getitem__'):
|
||||||
|
cutline_polygons = [cutline_polygons]
|
||||||
|
|
||||||
largest_cutline = cutline_polygons[0]
|
largest_cutline = cutline_polygons[0]
|
||||||
max_area = largest_cutline.area
|
max_area = largest_cutline.area
|
||||||
for p in cutline_polygons:
|
for p in cutline_polygons:
|
||||||
|
|
Ładowanie…
Reference in New Issue