kopia lustrzana https://github.com/OpenDroneMap/ODM
Porównaj commity
56 Commity
Autor | SHA1 | Data |
---|---|---|
![]() |
633116e8b5 | |
![]() |
9300c37f1e | |
![]() |
465b66a9ae | |
![]() |
25bb7b4487 | |
![]() |
415656a9b0 | |
![]() |
22aafb04d7 | |
![]() |
17c98a15af | |
![]() |
6114e5e934 | |
![]() |
831d4a26b4 | |
![]() |
cdd4647300 | |
![]() |
75e1be5b66 | |
![]() |
5552dcc09f | |
![]() |
ebe0ced583 | |
![]() |
cd9631b039 | |
![]() |
e4e1eca5d5 | |
![]() |
db14127e05 | |
![]() |
83d636a52a | |
![]() |
b965aef6b6 | |
![]() |
85d34c52f9 | |
![]() |
5e685d63a2 | |
![]() |
b2668ca738 | |
![]() |
131b29c3f3 | |
![]() |
ece421faec | |
![]() |
ce839d439a | |
![]() |
a68bfe319c | |
![]() |
ade17cc957 | |
![]() |
50add9bed5 | |
![]() |
a788cd3f73 | |
![]() |
6d1e800a0f | |
![]() |
b47c6ef2f9 | |
![]() |
6f7c533552 | |
![]() |
f5869777ed | |
![]() |
2190a5dfa0 | |
![]() |
175d141ea7 | |
![]() |
2215e7ab7a | |
![]() |
9c740cbf1b | |
![]() |
0333e08ce0 | |
![]() |
cbe8ed7580 | |
![]() |
75b9b92bbc | |
![]() |
df0c58574f | |
![]() |
36897fc0e9 | |
![]() |
e615914914 | |
![]() |
5aca98ba1e | |
![]() |
d58835bfdf | |
![]() |
12e5b63b6f | |
![]() |
5de1ff0258 | |
![]() |
e3a11fd727 | |
![]() |
4022d38219 | |
![]() |
5e1c9dd483 | |
![]() |
65fad52506 | |
![]() |
d87fff3aa2 | |
![]() |
9d7c1a2be5 | |
![]() |
08d351bd00 | |
![]() |
9f1bed5aaa | |
![]() |
af552c15e7 | |
![]() |
16ffec9034 |
|
@ -13,6 +13,7 @@ jobs:
|
|||
with:
|
||||
ghToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
openAI: ${{ secrets.OPENAI_TOKEN }}
|
||||
model: gpt-4o-2024-08-06
|
||||
filter: |
|
||||
- "#"
|
||||
variables: |
|
||||
|
|
|
@ -15,7 +15,13 @@ If you would rather not type commands in a shell and are looking for a friendly
|
|||
|
||||
## Quickstart
|
||||
|
||||
The easiest way to run ODM on is via docker. To install docker, see [docs.docker.com](https://docs.docker.com). Once you have docker installed and [working](https://docs.docker.com/get-started/#test-docker-installation), you can run ODM by placing some images (JPEGs or TIFFs) in a folder named “images” (for example `C:\Users\youruser\datasets\project\images` or `/home/youruser/datasets/project/images`) and simply run from a Command Prompt / Terminal:
|
||||
The easiest way to run ODM is via docker. To install docker, see [docs.docker.com](https://docs.docker.com). Once you have docker installed and [working](https://docs.docker.com/get-started/#test-docker-installation), you can get ODM by running from a Command Prompt / Terminal:
|
||||
|
||||
```bash
|
||||
docker pull opendronemap/odm
|
||||
```
|
||||
|
||||
Run ODM by placing some images (JPEGs, TIFFs or DNGs) in a folder named “images” (for example `C:\Users\youruser\datasets\project\images` or `/home/youruser/datasets/project/images`) and simply run from a Command Prompt / Terminal:
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
|
|
|
@ -244,7 +244,7 @@ externalproject_add(dem2points
|
|||
externalproject_add(odm_orthophoto
|
||||
DEPENDS opencv
|
||||
GIT_REPOSITORY https://github.com/OpenDroneMap/odm_orthophoto.git
|
||||
GIT_TAG 353
|
||||
GIT_TAG 355
|
||||
PREFIX ${SB_BINARY_DIR}/odm_orthophoto
|
||||
SOURCE_DIR ${SB_SOURCE_DIR}/odm_orthophoto
|
||||
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${SB_INSTALL_DIR}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
set(_proj_name obj2tiles)
|
||||
set(_SB_BINARY_DIR "${SB_BINARY_DIR}/${_proj_name}")
|
||||
|
||||
set(OBJ2TILES_VERSION v1.0.12)
|
||||
set(OBJ2TILES_VERSION v1.0.13)
|
||||
set(OBJ2TILES_EXT "")
|
||||
|
||||
set(OBJ2TILES_ARCH "Linux64")
|
||||
|
@ -9,7 +9,7 @@ if (WIN32)
|
|||
set(OBJ2TILES_ARCH "Win64")
|
||||
set(OBJ2TILES_EXT ".exe")
|
||||
elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
|
||||
set(OBJ2TILES_ARCH "LinuxArm")
|
||||
set(OBJ2TILES_ARCH "LinuxArm64")
|
||||
elseif(APPLE)
|
||||
set(OBJ2TILES_ARCH "Osx64")
|
||||
endif()
|
||||
|
|
|
@ -53,7 +53,7 @@ ExternalProject_Add(${_proj_name}
|
|||
#--Download step--------------
|
||||
DOWNLOAD_DIR ${SB_DOWNLOAD_DIR}
|
||||
GIT_REPOSITORY https://github.com/OpenDroneMap/openMVS
|
||||
GIT_TAG 320
|
||||
GIT_TAG 355
|
||||
#--Update/Patch step----------
|
||||
UPDATE_COMMAND ""
|
||||
#--Configure step-------------
|
||||
|
|
|
@ -25,7 +25,7 @@ ExternalProject_Add(${_proj_name}
|
|||
#--Download step--------------
|
||||
DOWNLOAD_DIR ${SB_DOWNLOAD_DIR}
|
||||
GIT_REPOSITORY https://github.com/OpenDroneMap/OpenSfM/
|
||||
GIT_TAG 352
|
||||
GIT_TAG 355
|
||||
#--Update/Patch step----------
|
||||
UPDATE_COMMAND git submodule update --init --recursive
|
||||
#--Configure step-------------
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
3.5.4
|
||||
3.5.5
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import bpy
|
||||
import materials_utils
|
||||
|
||||
def loadMesh(file):
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# DEM Blending
|
||||
|
||||
Blend sets of DEMs by calculating euclidean distance to null values and weighting the combination of elevation models. Based on the split-merge tool within ODM.
|
||||
|
||||
Requirements:
|
||||
* Directory full of images to blend together
|
||||
* NoData should be coded as a value of -9999
|
||||
|
||||
## Usage
|
||||
|
||||
```BASH
|
||||
docker run -ti --rm -v /home/youruser/folder_with_dems:/input --entrypoint /code/contrib/dem-blend/dem-blend.py opendronemap/odm /input
|
||||
```
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env python3
|
||||
# Authors: Piero Toffanin, Stephen Mather
|
||||
# License: AGPLv3
|
||||
|
||||
import os
|
||||
import glob
|
||||
import sys
|
||||
sys.path.insert(0, os.path.join("..", "..", os.path.dirname(__file__)))
|
||||
|
||||
import argparse
|
||||
from opendm.dem import merge
|
||||
|
||||
parser = argparse.ArgumentParser(description='Merge and blend DEMs using OpenDroneMap\'s approach.')
|
||||
parser.add_argument('input_dems',
|
||||
type=str,
|
||||
help='Path to input dems (.tif)')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not os.path.exists(args.input_dems):
|
||||
print("%s does not exist" % args.input_dems)
|
||||
exit(1)
|
||||
|
||||
output_dem = os.path.join(args.input_dems, 'merged_blended_dem.tif')
|
||||
input_dem_path = os.path.join(args.input_dems, '*.tif')
|
||||
input_dems = glob.glob(input_dem_path)
|
||||
|
||||
merge.euclidean_merge_dems(input_dems
|
||||
,output_dem=output_dem
|
||||
)
|
|
@ -4,7 +4,7 @@ import os
|
|||
|
||||
import PIL
|
||||
|
||||
from PIL import Image, ExifTags
|
||||
from PIL import Image
|
||||
|
||||
import shutil
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import numpy as np
|
|||
import numpy.ma as ma
|
||||
import multiprocessing
|
||||
import argparse
|
||||
import functools
|
||||
from skimage.draw import line
|
||||
from opensfm import dataset
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ COPY --from=builder /code /code
|
|||
# Copy the Python libraries installed via pip from the builder
|
||||
COPY --from=builder /usr/local /usr/local
|
||||
#COPY --from=builder /usr/lib/x86_64-linux-gnu/libavcodec.so.58 /usr/lib/x86_64-linux-gnu/libavcodec.so.58
|
||||
RUN dpkg --remove cuda-compat-11-2
|
||||
RUN apt-get update -y \
|
||||
&& apt-get install -y ffmpeg libtbb2
|
||||
# Install shared libraries that we depend on via APT, but *not*
|
||||
|
|
19
opendm/ai.py
19
opendm/ai.py
|
@ -4,6 +4,25 @@ from opendm import log
|
|||
import zipfile
|
||||
import time
|
||||
import sys
|
||||
import rawpy
|
||||
import cv2
|
||||
|
||||
def read_image(img_path):
|
||||
if img_path[-4:].lower() in [".dng", ".raw", ".nef"]:
|
||||
try:
|
||||
with rawpy.imread(img_path) as r:
|
||||
img = r.postprocess(output_bps=8, use_camera_wb=True, use_auto_wb=False)
|
||||
except:
|
||||
return None
|
||||
else:
|
||||
img = cv2.imread(img_path, cv2.IMREAD_COLOR)
|
||||
if img is None:
|
||||
return None
|
||||
|
||||
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
||||
|
||||
return img
|
||||
|
||||
|
||||
def get_model(namespace, url, version, name = "model.onnx"):
|
||||
version = version.replace(".", "_")
|
||||
|
|
|
@ -5,6 +5,7 @@ import cv2
|
|||
import os
|
||||
import onnxruntime as ort
|
||||
from opendm import log
|
||||
from opendm.ai import read_image
|
||||
from threading import Lock
|
||||
|
||||
mutex = Lock()
|
||||
|
@ -73,11 +74,7 @@ class BgFilter():
|
|||
return output
|
||||
|
||||
def run_img(self, img_path, dest):
|
||||
img = cv2.imread(img_path, cv2.IMREAD_COLOR)
|
||||
if img is None:
|
||||
return None
|
||||
|
||||
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
||||
img = read_image(img_path)
|
||||
mask = self.get_mask(img)
|
||||
|
||||
img_name = os.path.basename(img_path)
|
||||
|
|
|
@ -127,7 +127,7 @@ def url_string(string):
|
|||
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
|
||||
r'(?::\d+)?' # optional port
|
||||
r'(?:/?|[/?]\S+)$', re.IGNORECASE)
|
||||
|
||||
|
||||
if re.match(regex, string) is None:
|
||||
raise argparse.ArgumentTypeError("%s is not a valid URL. The URL must be in the format: http(s)://host[:port]/[?token=]" % string)
|
||||
return string
|
||||
|
@ -164,7 +164,7 @@ def config(argv=None, parser=None):
|
|||
parser = SettingsParser(description='ODM is a command line toolkit to generate maps, point clouds, 3D models and DEMs from drone, balloon or kite images.',
|
||||
usage='%s [options] <dataset name>' % usage_bin,
|
||||
yaml_file=open(context.settings_path))
|
||||
|
||||
|
||||
parser.add_argument('--project-path',
|
||||
metavar='<path>',
|
||||
action=StoreValue,
|
||||
|
@ -213,7 +213,7 @@ def config(argv=None, parser=None):
|
|||
'More features can be useful for finding more matches between images, '
|
||||
'potentially allowing the reconstruction of areas with little overlap or insufficient features. '
|
||||
'More features also slow down processing. Default: %(default)s'))
|
||||
|
||||
|
||||
parser.add_argument('--feature-type',
|
||||
metavar='<string>',
|
||||
action=StoreValue,
|
||||
|
@ -222,7 +222,7 @@ def config(argv=None, parser=None):
|
|||
help=('Choose the algorithm for extracting keypoints and computing descriptors. '
|
||||
'Can be one of: %(choices)s. Default: '
|
||||
'%(default)s'))
|
||||
|
||||
|
||||
parser.add_argument('--feature-quality',
|
||||
metavar='<string>',
|
||||
action=StoreValue,
|
||||
|
@ -231,7 +231,7 @@ def config(argv=None, parser=None):
|
|||
help=('Set feature extraction quality. Higher quality generates better features, but requires more memory and takes longer. '
|
||||
'Can be one of: %(choices)s. Default: '
|
||||
'%(default)s'))
|
||||
|
||||
|
||||
parser.add_argument('--matcher-type',
|
||||
metavar='<string>',
|
||||
action=StoreValue,
|
||||
|
@ -247,7 +247,7 @@ def config(argv=None, parser=None):
|
|||
default=0,
|
||||
type=int,
|
||||
help='Perform image matching with the nearest images based on GPS exif data. Set to 0 to match by triangulation. Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--matcher-order',
|
||||
metavar='<positive integer>',
|
||||
action=StoreValue,
|
||||
|
@ -331,7 +331,7 @@ def config(argv=None, parser=None):
|
|||
nargs=0,
|
||||
default=False,
|
||||
help='Automatically compute image masks using AI to remove the sky. Experimental. Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--bg-removal',
|
||||
action=StoreTrue,
|
||||
nargs=0,
|
||||
|
@ -349,19 +349,19 @@ def config(argv=None, parser=None):
|
|||
nargs=0,
|
||||
default=False,
|
||||
help='Skip generation of a full 3D model. This can save time if you only need 2D results such as orthophotos and DEMs. Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--skip-report',
|
||||
action=StoreTrue,
|
||||
nargs=0,
|
||||
default=False,
|
||||
help='Skip generation of PDF report. This can save time if you don\'t need a report. Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--skip-orthophoto',
|
||||
action=StoreTrue,
|
||||
nargs=0,
|
||||
default=False,
|
||||
help='Skip generation of the orthophoto. This can save time if you only need 3D results or DEMs. Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--ignore-gsd',
|
||||
action=StoreTrue,
|
||||
nargs=0,
|
||||
|
@ -371,13 +371,13 @@ def config(argv=None, parser=None):
|
|||
'Ordinarily, GSD estimates are used to cap the maximum resolution of image outputs and resizes images when necessary, resulting in faster processing and lower memory usage. '
|
||||
'Since GSD is an estimate, sometimes ignoring it can result in slightly better image output quality. '
|
||||
'Never set --ignore-gsd to true unless you are positive you need it, and even then: do not use it. Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--no-gpu',
|
||||
action=StoreTrue,
|
||||
nargs=0,
|
||||
default=False,
|
||||
help='Do not use GPU acceleration, even if it\'s available. Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--mesh-size',
|
||||
metavar='<positive integer>',
|
||||
action=StoreValue,
|
||||
|
@ -462,7 +462,7 @@ def config(argv=None, parser=None):
|
|||
nargs=0,
|
||||
default=False,
|
||||
help='Export the georeferenced point cloud in CSV format. Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--pc-las',
|
||||
action=StoreTrue,
|
||||
nargs=0,
|
||||
|
@ -488,7 +488,7 @@ def config(argv=None, parser=None):
|
|||
default=5,
|
||||
help='Filters the point cloud by removing points that deviate more than N standard deviations from the local mean. Set to 0 to disable filtering. '
|
||||
'Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--pc-sample',
|
||||
metavar='<positive float>',
|
||||
action=StoreValue,
|
||||
|
@ -519,7 +519,7 @@ def config(argv=None, parser=None):
|
|||
default=0.15,
|
||||
help='Simple Morphological Filter slope parameter (rise over run). '
|
||||
'Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--smrf-threshold',
|
||||
metavar='<positive float>',
|
||||
action=StoreValue,
|
||||
|
@ -527,7 +527,7 @@ def config(argv=None, parser=None):
|
|||
default=0.5,
|
||||
help='Simple Morphological Filter elevation threshold parameter (meters). '
|
||||
'Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--smrf-window',
|
||||
metavar='<positive float>',
|
||||
action=StoreValue,
|
||||
|
@ -586,7 +586,7 @@ def config(argv=None, parser=None):
|
|||
'EPSG:<code> or <+proj definition>\n'
|
||||
'image_name geo_x geo_y geo_z [yaw (degrees)] [pitch (degrees)] [roll (degrees)] [horz accuracy (meters)] [vert accuracy (meters)]\n'
|
||||
'Default: %(default)s'))
|
||||
|
||||
|
||||
parser.add_argument('--align',
|
||||
metavar='<path string>',
|
||||
action=StoreValue,
|
||||
|
@ -642,7 +642,7 @@ def config(argv=None, parser=None):
|
|||
type=int,
|
||||
help='Decimate the points before generating the DEM. 1 is no decimation (full quality). '
|
||||
'100 decimates ~99%% of the points. Useful for speeding up generation of DEM results in very large datasets. Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--dem-euclidean-map',
|
||||
action=StoreTrue,
|
||||
nargs=0,
|
||||
|
@ -675,13 +675,13 @@ def config(argv=None, parser=None):
|
|||
default=False,
|
||||
help='Set this parameter if you want to generate a PNG rendering of the orthophoto. '
|
||||
'Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--orthophoto-kmz',
|
||||
action=StoreTrue,
|
||||
nargs=0,
|
||||
default=False,
|
||||
help='Set this parameter if you want to generate a Google Earth (KMZ) rendering of the orthophoto. '
|
||||
'Default: %(default)s')
|
||||
'Default: %(default)s')
|
||||
|
||||
parser.add_argument('--orthophoto-compression',
|
||||
metavar='<string>',
|
||||
|
@ -690,7 +690,7 @@ def config(argv=None, parser=None):
|
|||
choices=['JPEG', 'LZW', 'PACKBITS', 'DEFLATE', 'LZMA', 'NONE'],
|
||||
default='DEFLATE',
|
||||
help='Set the compression to use for orthophotos. Can be one of: %(choices)s. Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--orthophoto-cutline',
|
||||
action=StoreTrue,
|
||||
nargs=0,
|
||||
|
@ -729,7 +729,7 @@ def config(argv=None, parser=None):
|
|||
action=StoreValue,
|
||||
metavar='<positive integer>',
|
||||
default=0,
|
||||
help='Override the rolling shutter readout time for your camera sensor (in milliseconds), instead of using the rolling shutter readout database. '
|
||||
help='Override the rolling shutter readout time for your camera sensor (in milliseconds), instead of using the rolling shutter readout database. '
|
||||
'Note that not all cameras are present in the database. Set to 0 to use the database value. '
|
||||
'Default: %(default)s')
|
||||
|
||||
|
@ -768,7 +768,7 @@ def config(argv=None, parser=None):
|
|||
default=4000,
|
||||
metavar='<positive integer>',
|
||||
help='The maximum output resolution of extracted video frames in pixels. Default: %(default)s')
|
||||
|
||||
|
||||
parser.add_argument('--split',
|
||||
type=int,
|
||||
action=StoreValue,
|
||||
|
@ -785,11 +785,12 @@ def config(argv=None, parser=None):
|
|||
action=StoreValue,
|
||||
metavar='<positive integer>',
|
||||
default=150,
|
||||
help='Radius of the overlap between submodels. '
|
||||
help='Radius of the overlap between submodels in meters. '
|
||||
'After grouping images into clusters, images '
|
||||
'that are closer than this radius to a cluster '
|
||||
'are added to the cluster. This is done to ensure '
|
||||
'that neighboring submodels overlap. Default: %(default)s')
|
||||
'that neighboring submodels overlap. All images' \
|
||||
'need GPS information. Default: %(default)s')
|
||||
|
||||
parser.add_argument('--split-image-groups',
|
||||
metavar='<path string>',
|
||||
|
@ -833,7 +834,7 @@ def config(argv=None, parser=None):
|
|||
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'))
|
||||
|
||||
|
||||
parser.add_argument('--gps-accuracy',
|
||||
type=float,
|
||||
action=StoreValue,
|
||||
|
@ -868,7 +869,7 @@ def config(argv=None, parser=None):
|
|||
default="auto",
|
||||
type=str,
|
||||
help=('When processing multispectral datasets, you can specify the name of the primary band that will be used for reconstruction. '
|
||||
'It\'s recommended to choose a band which has sharp details and is in focus. '
|
||||
'It\'s recommended to choose a band which has sharp details and is in focus. '
|
||||
'Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--skip-band-alignment',
|
||||
|
@ -923,5 +924,5 @@ def config(argv=None, parser=None):
|
|||
except exceptions.NodeConnectionError as e:
|
||||
log.ODM_ERROR("Cluster node seems to be offline: %s" % str(e))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
return args
|
||||
|
|
|
@ -40,7 +40,7 @@ odm_orthophoto_path = os.path.join(superbuild_bin_path, "odm_orthophoto")
|
|||
settings_path = os.path.join(root_path, 'settings.yaml')
|
||||
|
||||
# Define supported image extensions
|
||||
supported_extensions = {'.jpg','.jpeg','.png', '.tif', '.tiff', '.bmp'}
|
||||
supported_extensions = {'.jpg','.jpeg','.png', '.tif', '.tiff', '.bmp', '.raw', '.dng', '.nef'}
|
||||
supported_video_extensions = {'.mp4', '.mov', '.lrv', '.ts'}
|
||||
|
||||
# Define the number of cores
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import rasterio
|
||||
import numpy
|
||||
|
@ -20,6 +21,8 @@ from opendm import log
|
|||
from .ground_rectification.rectify import run_rectification
|
||||
from . import pdal
|
||||
|
||||
gdal_proximity = None
|
||||
|
||||
try:
|
||||
# GDAL >= 3.3
|
||||
from osgeo_utils.gdal_proximity import main as gdal_proximity
|
||||
|
@ -27,8 +30,13 @@ except ModuleNotFoundError:
|
|||
# GDAL <= 3.2
|
||||
try:
|
||||
from osgeo.utils.gdal_proximity import main as gdal_proximity
|
||||
except:
|
||||
pass
|
||||
except ModuleNotFoundError:
|
||||
# GDAL <= 3.0
|
||||
gdal_proximity_script = shutil.which("gdal_proximity.py")
|
||||
if gdal_proximity_script is not None:
|
||||
def gdal_proximity(args):
|
||||
subprocess.run([gdal_proximity_script] + args[1:], check=True)
|
||||
|
||||
|
||||
def classify(lasFile, scalar, slope, threshold, window):
|
||||
start = datetime.now()
|
||||
|
@ -263,4 +271,4 @@ def get_dem_radius_steps(stats_file, steps, resolution, multiplier = 1.0):
|
|||
for _ in range(steps - 1):
|
||||
radius_steps.append(radius_steps[-1] * math.sqrt(2))
|
||||
|
||||
return radius_steps
|
||||
return radius_steps
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from PIL import Image
|
||||
import cv2
|
||||
|
||||
import rawpy
|
||||
from opendm import log
|
||||
|
||||
Image.MAX_IMAGE_PIXELS = None
|
||||
|
@ -9,12 +9,18 @@ def get_image_size(file_path, fallback_on_error=True):
|
|||
"""
|
||||
Return (width, height) for a given img file
|
||||
"""
|
||||
|
||||
try:
|
||||
with Image.open(file_path) as img:
|
||||
width, height = img.size
|
||||
if file_path[-4:].lower() in [".dng", ".raw", ".nef"]:
|
||||
with rawpy.imread(file_path) as img:
|
||||
s = img.sizes
|
||||
width, height = s.raw_width, s.raw_height
|
||||
else:
|
||||
with Image.open(file_path) as img:
|
||||
width, height = img.size
|
||||
except Exception as e:
|
||||
if fallback_on_error:
|
||||
log.ODM_WARNING("Cannot read %s with PIL, fallback to cv2: %s" % (file_path, str(e)))
|
||||
log.ODM_WARNING("Cannot read %s with image library, fallback to cv2: %s" % (file_path, str(e)))
|
||||
img = cv2.imread(file_path)
|
||||
width = img.shape[1]
|
||||
height = img.shape[0]
|
||||
|
|
|
@ -6,6 +6,7 @@ import numpy as np
|
|||
import pygltflib
|
||||
from opendm import system
|
||||
from opendm import io
|
||||
from opendm import log
|
||||
|
||||
warnings.filterwarnings("ignore", category=rasterio.errors.NotGeoreferencedWarning)
|
||||
|
||||
|
|
|
@ -273,7 +273,10 @@ def compute_band_maps(multi_camera, primary_band):
|
|||
# Quick check
|
||||
if filename_without_band == p.filename:
|
||||
raise Exception("Cannot match bands by filename on %s, make sure to name your files [filename]_band[.ext] uniformly." % p.filename)
|
||||
|
||||
|
||||
if not filename_without_band in filename_map:
|
||||
raise Exception("Cannot match bands by filename on %s, make sure to name your files [filename]_band[.ext] uniformly, check that your images have the appropriate CaptureUUID XMP tag and that no images are missing." % p.filename)
|
||||
|
||||
s2p[p.filename] = filename_map[filename_without_band]
|
||||
|
||||
if band['name'] != band_name:
|
||||
|
|
|
@ -14,6 +14,7 @@ from opendm import io
|
|||
from opendm.tiles.tiler import generate_orthophoto_tiles
|
||||
from opendm.cogeo import convert_to_cogeo
|
||||
from osgeo import gdal
|
||||
from osgeo import ogr
|
||||
|
||||
|
||||
def get_orthophoto_vars(args):
|
||||
|
@ -43,33 +44,50 @@ def generate_png(orthophoto_file, output_file=None, outsize=None):
|
|||
output_file = base + '.png'
|
||||
|
||||
# See if we need to select top three bands
|
||||
bandparam = ""
|
||||
params = []
|
||||
|
||||
gtif = gdal.Open(orthophoto_file)
|
||||
if gtif.RasterCount > 4:
|
||||
try:
|
||||
gtif = gdal.Open(orthophoto_file)
|
||||
bands = []
|
||||
for idx in range(1, gtif.RasterCount+1):
|
||||
bands.append(gtif.GetRasterBand(idx).GetColorInterpretation())
|
||||
bands = dict(zip(bands, range(1, len(bands)+1)))
|
||||
|
||||
try:
|
||||
if gtif.RasterCount >= 3:
|
||||
red = bands.get(gdal.GCI_RedBand)
|
||||
green = bands.get(gdal.GCI_GreenBand)
|
||||
blue = bands.get(gdal.GCI_BlueBand)
|
||||
if red is None or green is None or blue is None:
|
||||
raise Exception("Cannot find bands")
|
||||
params.append("-b 1 -b 2 -b 3")
|
||||
else:
|
||||
params.append("-b %s -b %s -b %s" % (red, green, blue))
|
||||
elif gtif.RasterCount <= 2:
|
||||
params.append("-b 1")
|
||||
|
||||
alpha = bands.get(gdal.GCI_AlphaBand)
|
||||
if alpha is not None:
|
||||
params.append("-b %s" % alpha)
|
||||
else:
|
||||
params.append("-a_nodata 0")
|
||||
|
||||
bandparam = "-b %s -b %s -b %s -a_nodata 0" % (red, green, blue)
|
||||
except:
|
||||
bandparam = "-b 1 -b 2 -b 3 -a_nodata 0"
|
||||
gtif = None
|
||||
dtype = gtif.GetRasterBand(1).DataType
|
||||
if dtype != gdal.GDT_Byte:
|
||||
params.append("-ot Byte")
|
||||
if gtif.RasterCount >= 3:
|
||||
params.append("-scale_1 -scale_2 -scale_3")
|
||||
elif gtif.RasterCount <= 2:
|
||||
params.append("-scale_1")
|
||||
|
||||
gtif = None
|
||||
except Exception as e:
|
||||
log.ODM_WARNING("Cannot read orthophoto information for PNG generation: %s" % str(e))
|
||||
|
||||
osparam = ""
|
||||
if outsize is not None:
|
||||
osparam = "-outsize %s 0" % outsize
|
||||
params.append("-outsize %s 0" % outsize)
|
||||
|
||||
system.run('gdal_translate -of png "%s" "%s" %s %s '
|
||||
'--config GDAL_CACHEMAX %s%% ' % (orthophoto_file, output_file, osparam, bandparam, get_max_memory()))
|
||||
system.run('gdal_translate -of png "%s" "%s" %s '
|
||||
'-co WORLDFILE=YES '
|
||||
'--config GDAL_CACHEMAX %s%% ' % (orthophoto_file, output_file, " ".join(params), get_max_memory()))
|
||||
|
||||
def generate_kmz(orthophoto_file, output_file=None, outsize=None):
|
||||
if output_file is None:
|
||||
|
@ -84,7 +102,70 @@ def generate_kmz(orthophoto_file, output_file=None, outsize=None):
|
|||
|
||||
system.run('gdal_translate -of KMLSUPEROVERLAY -co FORMAT=PNG "%s" "%s" %s '
|
||||
'--config GDAL_CACHEMAX %s%% ' % (orthophoto_file, output_file, bandparam, get_max_memory()))
|
||||
|
||||
|
||||
def generate_extent_polygon(orthophoto_file):
|
||||
"""Function to return the orthophoto extent as a polygon into a gpkg file
|
||||
|
||||
Args:
|
||||
orthophoto_file (str): the path to orthophoto file
|
||||
"""
|
||||
base, ext = os.path.splitext(orthophoto_file)
|
||||
output_file = base + '_extent.dxf'
|
||||
|
||||
try:
|
||||
gtif = gdal.Open(orthophoto_file)
|
||||
srs = gtif.GetSpatialRef()
|
||||
geoTransform = gtif.GetGeoTransform()
|
||||
|
||||
# calculate the coordinates
|
||||
minx = geoTransform[0]
|
||||
maxy = geoTransform[3]
|
||||
maxx = minx + geoTransform[1] * gtif.RasterXSize
|
||||
miny = maxy + geoTransform[5] * gtif.RasterYSize
|
||||
|
||||
# create polygon in wkt format
|
||||
poly_wkt = "POLYGON ((%s %s, %s %s, %s %s, %s %s, %s %s))" % (minx, miny, minx, maxy, maxx, maxy, maxx, miny, minx, miny)
|
||||
|
||||
# create vector file
|
||||
# just the DXF to support AutoCAD users
|
||||
# to load the geotiff raster correctly.
|
||||
driver = ogr.GetDriverByName("DXF")
|
||||
ds = driver.CreateDataSource(output_file)
|
||||
layer = ds.CreateLayer("extent", srs, ogr.wkbPolygon)
|
||||
|
||||
# create the feature and set values
|
||||
featureDefn = layer.GetLayerDefn()
|
||||
feature = ogr.Feature(featureDefn)
|
||||
feature.SetGeometry(ogr.CreateGeometryFromWkt(poly_wkt))
|
||||
|
||||
# add feature to layer
|
||||
layer.CreateFeature(feature)
|
||||
|
||||
# save and close everything
|
||||
feature = None
|
||||
ds = None
|
||||
gtif = None
|
||||
log.ODM_INFO("Wrote %s" % output_file)
|
||||
except Exception as e:
|
||||
log.ODM_WARNING("Cannot create extent layer for %s: %s" % (orthophoto_file, str(e)))
|
||||
|
||||
|
||||
def generate_tfw(orthophoto_file):
|
||||
base, ext = os.path.splitext(orthophoto_file)
|
||||
tfw_file = base + '.tfw'
|
||||
|
||||
try:
|
||||
with rasterio.open(orthophoto_file) as ds:
|
||||
t = ds.transform
|
||||
with open(tfw_file, 'w') as f:
|
||||
# rasterio affine values taken by
|
||||
# https://mharty3.github.io/til/GIS/raster-affine-transforms/
|
||||
f.write("\n".join([str(v) for v in [t.a, t.d, t.b, t.e, t.c, t.f]]) + "\n")
|
||||
log.ODM_INFO("Wrote %s" % tfw_file)
|
||||
except Exception as e:
|
||||
log.ODM_WARNING("Cannot create .tfw for %s: %s" % (orthophoto_file, str(e)))
|
||||
|
||||
|
||||
def post_orthophoto_steps(args, bounds_file_path, orthophoto_file, orthophoto_tiles_dir, resolution):
|
||||
if args.crop > 0 or args.boundary:
|
||||
Cropper.crop(bounds_file_path, orthophoto_file, get_orthophoto_vars(args), keep_original=not args.optimize_disk_space, warp_options=['-dstalpha'])
|
||||
|
@ -104,6 +185,9 @@ def post_orthophoto_steps(args, bounds_file_path, orthophoto_file, orthophoto_ti
|
|||
if args.cog:
|
||||
convert_to_cogeo(orthophoto_file, max_workers=args.max_concurrency, compression=args.orthophoto_compression)
|
||||
|
||||
generate_extent_polygon(orthophoto_file)
|
||||
generate_tfw(orthophoto_file)
|
||||
|
||||
def compute_mask_raster(input_raster, vector_mask, output_raster, blend_distance=20, only_max_coords_feature=False):
|
||||
if not os.path.exists(input_raster):
|
||||
log.ODM_WARNING("Cannot mask raster, %s does not exist" % input_raster)
|
||||
|
|
|
@ -327,7 +327,7 @@ def post_point_cloud_steps(args, tree, rerun=False):
|
|||
tree.odm_georeferencing_model_laz,
|
||||
tree.odm_georeferencing_model_las))
|
||||
else:
|
||||
log.ODM_WARNING("Found existing LAS file %s" % tree.odm_georeferencing_xyz_file)
|
||||
log.ODM_WARNING("Found existing LAS file %s" % tree.odm_georeferencing_model_las)
|
||||
|
||||
# EPT point cloud output
|
||||
if args.pc_ept:
|
||||
|
|
|
@ -6,6 +6,7 @@ import os
|
|||
import onnxruntime as ort
|
||||
from .guidedfilter import guided_filter
|
||||
from opendm import log
|
||||
from opendm.ai import read_image
|
||||
from threading import Lock
|
||||
|
||||
mutex = Lock()
|
||||
|
@ -72,11 +73,7 @@ class SkyFilter():
|
|||
|
||||
def run_img(self, img_path, dest):
|
||||
|
||||
img = cv2.imread(img_path, cv2.IMREAD_COLOR)
|
||||
if img is None:
|
||||
return None
|
||||
|
||||
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
||||
img = read_image(img_path)
|
||||
img = np.array(img / 255., dtype=np.float32)
|
||||
|
||||
mask = self.get_mask(img)
|
||||
|
|
|
@ -120,6 +120,7 @@ class SrtFileParser:
|
|||
# <font size="36">SrtCnt : 1, DiffTime : 16ms
|
||||
# 2023-01-06 18:56:48,380,821
|
||||
# [iso : 3200] [shutter : 1/60.0] [fnum : 280] [ev : 0] [ct : 3925] [color_md : default] [focal_len : 240] [latitude: 0.000000] [longitude: 0.000000] [altitude: 0.000000] </font>
|
||||
# </font>
|
||||
|
||||
# DJI Mavic Mini
|
||||
# 1
|
||||
|
@ -164,9 +165,10 @@ class SrtFileParser:
|
|||
end = None
|
||||
|
||||
for line in f:
|
||||
# Remove html tags, spaces
|
||||
line = re.sub('<[^<]+?>', '', line).strip()
|
||||
|
||||
# Check if line is empty
|
||||
if not line.strip():
|
||||
if not line:
|
||||
if start is not None:
|
||||
self.data.append({
|
||||
"start": start,
|
||||
|
@ -193,9 +195,6 @@ class SrtFileParser:
|
|||
|
||||
continue
|
||||
|
||||
# Remove html tags
|
||||
line = re.sub('<[^<]+?>', '', line)
|
||||
|
||||
# Search this "00:00:00,000 --> 00:00:00,016"
|
||||
match = re.search("(\d{2}:\d{2}:\d{2},\d+) --> (\d{2}:\d{2}:\d{2},\d+)", line)
|
||||
if match:
|
||||
|
@ -225,14 +224,14 @@ class SrtFileParser:
|
|||
("GPS \([\d\.\-]+,? ([\d\.\-]+),? [\d\.\-]+\)", lambda v: float(v) if v != 0 else None),
|
||||
("RTK \([-+]?\d+\.\d+, (-?\d+\.\d+), -?\d+\)", lambda v: float(v) if v != 0 else None),
|
||||
], line)
|
||||
|
||||
|
||||
longitude = match_single([
|
||||
("longitude: ([\d\.\-]+)", lambda v: float(v) if v != 0 else None),
|
||||
("longtitude : ([\d\.\-]+)", lambda v: float(v) if v != 0 else None),
|
||||
("GPS \(([\d\.\-]+),? [\d\.\-]+,? [\d\.\-]+\)", lambda v: float(v) if v != 0 else None),
|
||||
("RTK \((-?\d+\.\d+), [-+]?\d+\.\d+, -?\d+\)", lambda v: float(v) if v != 0 else None),
|
||||
], line)
|
||||
|
||||
|
||||
altitude = match_single([
|
||||
("altitude: ([\d\.\-]+)", lambda v: float(v) if v != 0 else None),
|
||||
("GPS \([\d\.\-]+,? [\d\.\-]+,? ([\d\.\-]+)\)", lambda v: float(v) if v != 0 else None),
|
||||
|
|
|
@ -23,6 +23,7 @@ rasterio==1.2.3 ; sys_platform == 'linux'
|
|||
rasterio==1.3.6 ; sys_platform == 'darwin'
|
||||
https://github.com/OpenDroneMap/windows-deps/raw/main/rasterio-1.2.3-cp38-cp38-win_amd64.whl ; sys_platform == 'win32'
|
||||
https://github.com/OpenDroneMap/windows-deps/raw/main/GDAL-3.2.3-cp38-cp38-win_amd64.whl ; sys_platform == 'win32'
|
||||
odmrawpy==0.24.1
|
||||
repoze.lru==0.7
|
||||
scikit-learn==1.1.1
|
||||
Pywavelets==1.3.0
|
||||
|
|
|
@ -234,9 +234,9 @@ class ODMLoadDatasetStage(types.ODM_Stage):
|
|||
item['p'].set_mask(os.path.basename(mask_file))
|
||||
log.ODM_INFO("Wrote %s" % os.path.basename(mask_file))
|
||||
else:
|
||||
log.ODM_WARNING("Cannot generate mask for %s" % img)
|
||||
log.ODM_WARNING("Cannot generate mask for %s" % item['file'])
|
||||
except Exception as e:
|
||||
log.ODM_WARNING("Cannot generate mask for %s: %s" % (img, str(e)))
|
||||
log.ODM_WARNING("Cannot generate mask for %s: %s" % (item['file'], str(e)))
|
||||
|
||||
parallel_map(parallel_sky_filter, sky_images, max_workers=args.max_concurrency)
|
||||
|
||||
|
|
|
@ -63,11 +63,13 @@ class ODMOpenMVSStage(types.ODM_Stage):
|
|||
densify_ini_file = os.path.join(tree.openmvs, 'Densify.ini')
|
||||
subres_levels = 2 # The number of lower resolutions to process before estimating output resolution depthmap.
|
||||
filter_point_th = -20
|
||||
min_resolution = 320 if args.pc_quality in ["low", "lowest"] else 640
|
||||
|
||||
config = [
|
||||
"--resolution-level %s" % int(resolution_level),
|
||||
'--dense-config-file "%s"' % densify_ini_file,
|
||||
"--max-resolution %s" % int(outputs['undist_image_max_size']),
|
||||
"--min-resolution %s" % min_resolution,
|
||||
"--max-threads %s" % args.max_concurrency,
|
||||
"--number-views-fuse %s" % number_views_fuse,
|
||||
"--sub-resolution-levels %s" % subres_levels,
|
||||
|
|
|
@ -1,19 +1,15 @@
|
|||
import os
|
||||
import shutil
|
||||
import json
|
||||
import yaml
|
||||
from opendm import log
|
||||
from opendm.osfm import OSFMContext, get_submodel_argv, get_submodel_paths, get_all_submodel_paths
|
||||
from opendm import types
|
||||
from opendm import io
|
||||
from opendm import system
|
||||
from opendm import orthophoto
|
||||
from opendm.gcp import GCPFile
|
||||
from opendm.dem import pdal, utils
|
||||
from opendm.dem import utils
|
||||
from opendm.dem.merge import euclidean_merge_dems
|
||||
from opensfm.large import metadataset
|
||||
from opendm.cropper import Cropper
|
||||
from opendm.concurrency import get_max_memory
|
||||
from opendm.remote import LocalRemoteExecutor
|
||||
from opendm.shots import merge_geojson_shots, merge_cameras
|
||||
from opendm import point_cloud
|
||||
|
|
Ładowanie…
Reference in New Issue