Porównaj commity

...

33 Commity

Autor SHA1 Wiadomość Data
Piero Toffanin ae6726e536
Merge pull request #1760 from pierotofy/fastcut
Skip feathered raster generation when possible
2024-05-17 15:51:32 -04:00
Piero Toffanin 6da366f806 Windows fix 2024-05-17 15:23:10 -04:00
Piero Toffanin e4e27c21f2 Skip feathered raster generation when possible 2024-05-17 14:55:26 -04:00
Piero Toffanin f9136f7a0d
Merge pull request #1758 from idimitrovski/master
Support for DJI Mavic 2 Zoom srt files
2024-05-10 11:24:52 -04:00
idimitrovski a2d9eccad5 Support for DJI Mavic 2 Zoom srt files 2024-05-10 09:29:37 +02:00
Piero Toffanin 424d9e28a0
Merge pull request #1756 from andrewharvey/patch-1
Fix PoissonRecon failed with n threads log message
2024-04-18 11:53:46 -04:00
Andrew Harvey a0fbd71d41
Fix PoissonRecon failed with n threads log message
The message was reporting failure with n threads and retrying with n // 2, however a few lines up threads was already set to n // 2 representing the next thread count to try.
2024-04-18 15:35:53 +10:00
Piero Toffanin 6084d1dca0
Merge pull request #1754 from pierotofy/minviews
Use min views filter = 1
2024-04-11 23:18:26 -04:00
Piero Toffanin aef4182cf9 More solid OpenMVS clustering fallback 2024-04-11 14:18:20 -04:00
Piero Toffanin 6c0fe6e79d Bump version 2024-04-10 22:07:36 -04:00
Piero Toffanin 17dfc7599a Update pc-filter value to 5 2024-04-10 13:48:11 -04:00
Piero Toffanin a70e7445ad Update default feature-type, pc-filter values 2024-04-10 12:26:34 -04:00
Piero Toffanin 981bf88b48 Use min views filter = 1 2024-04-10 11:13:58 -04:00
Piero Toffanin ad63392e1a
Merge pull request #1752 from pierotofy/geobom
Fix BOM encoding bug with geo files
2024-04-02 12:53:35 -04:00
Piero Toffanin 77f8ffc8cd Fix BOM encoding bug with geo files 2024-04-02 12:46:20 -04:00
Piero Toffanin 4d7cf32a8c
Merge pull request #1751 from smathermather/fish-aye
replace fisheye with fisheye_opencv but keep API the same until 4.0
2024-03-11 23:32:19 -04:00
Stephen Mather 5a439c0ab6 replace fisheye with fisheye_opencv but keep API the same until 4.0 2024-03-11 22:56:48 -04:00
Piero Toffanin ffcda0dc57
Merge pull request #1749 from smathermather/increase-default-GPS-Accuracy
increase default GPS-Accuracy to 3m
2024-03-08 22:16:44 -05:00
Stephen Mather 2c6fd1dd9f
increase default GPS-Accuracy to 3m 2024-03-08 22:13:54 -05:00
Sylvain POULAIN cb3229a3d4
Add Mavic 3 rolling shutter, not enterprise version (#1747)
* Add Mavic 3 rolling shutter

* M3
2024-02-12 09:50:22 -05:00
Piero Toffanin fc9c94880f
Merge pull request #1746 from kielnino/set-extensionsused
GLTF - obj2glb - Set extensionsUsed in all cases to be consistent with the GLTF standard
2024-02-09 10:23:22 -05:00
kielnino b204a2eb98
set extensionsUsed in all cases 2024-02-09 15:06:02 +01:00
Piero Toffanin d9f77bea54
Merge pull request #1744 from kielnino/remove-unuses-mvs_tmp_dir
Update comment on mvs_tmp_dir
2024-02-01 09:14:23 -05:00
kielnino 10947ecddf
clarify usage of tmp directory 2024-02-01 12:02:06 +01:00
kielnino f7c7044823
remove unused mvs_tmp_dir 2024-02-01 09:25:10 +01:00
Piero Toffanin ae50133886
Merge pull request #1742 from pierotofy/eptclass
Classify point cloud before generating derivative outputs
2024-01-25 12:56:36 -05:00
Piero Toffanin 9fd3bf3edd Improve SRT parser to handle abs_alt altitude reference 2024-01-23 22:24:38 +00:00
Piero Toffanin fb85b754fb Classify point cloud before generating derivative outputs 2024-01-23 17:03:36 -05:00
Piero Toffanin 30f89c068c
Merge pull request #1739 from pierotofy/smartband
Fix build
2024-01-15 19:41:20 -05:00
Piero Toffanin 260b4ef864 Manually install numpy 2024-01-15 16:21:08 -05:00
Piero Toffanin fb5d88366e
Merge pull request #1738 from pierotofy/smartband
Ignore multispectral band groups that are missing images
2024-01-15 11:38:53 -05:00
Piero Toffanin f793627402 Ignore multispectral band groups that are missing images 2024-01-15 09:51:17 -05:00
Piero Toffanin 9183218f1b Bump version 2024-01-12 00:20:35 -05:00
16 zmienionych plików z 131 dodań i 56 usunięć

Wyświetl plik

@ -1 +1 @@
3.3.4 3.5.1

Wyświetl plik

@ -127,6 +127,9 @@ installreqs() {
installdepsfromsnapcraft build openmvs installdepsfromsnapcraft build openmvs
set -e set -e
# edt requires numpy to build
pip install --ignore-installed numpy==1.23.1
pip install --ignore-installed -r requirements.txt pip install --ignore-installed -r requirements.txt
#if [ ! -z "$GPU_INSTALL" ]; then #if [ ! -z "$GPU_INSTALL" ]; then
#fi #fi

Wyświetl plik

@ -60,14 +60,14 @@ rerun_stages = {
'orthophoto_no_tiled': 'odm_orthophoto', 'orthophoto_no_tiled': 'odm_orthophoto',
'orthophoto_png': 'odm_orthophoto', 'orthophoto_png': 'odm_orthophoto',
'orthophoto_resolution': 'odm_orthophoto', 'orthophoto_resolution': 'odm_orthophoto',
'pc_classify': 'odm_dem', 'pc_classify': 'odm_georeferencing',
'pc_copc': 'odm_georeferencing', 'pc_copc': 'odm_georeferencing',
'pc_csv': 'odm_georeferencing', 'pc_csv': 'odm_georeferencing',
'pc_ept': 'odm_georeferencing', 'pc_ept': 'odm_georeferencing',
'pc_filter': 'openmvs', 'pc_filter': 'openmvs',
'pc_las': 'odm_georeferencing', 'pc_las': 'odm_georeferencing',
'pc_quality': 'opensfm', 'pc_quality': 'opensfm',
'pc_rectify': 'odm_dem', 'pc_rectify': 'odm_georeferencing',
'pc_sample': 'odm_filterpoints', 'pc_sample': 'odm_filterpoints',
'pc_skip_geometric': 'openmvs', 'pc_skip_geometric': 'openmvs',
'primary_band': 'dataset', 'primary_band': 'dataset',
@ -217,7 +217,7 @@ def config(argv=None, parser=None):
parser.add_argument('--feature-type', parser.add_argument('--feature-type',
metavar='<string>', metavar='<string>',
action=StoreValue, action=StoreValue,
default='sift', default='dspsift',
choices=['akaze', 'dspsift', 'hahog', 'orb', 'sift'], choices=['akaze', 'dspsift', 'hahog', 'orb', 'sift'],
help=('Choose the algorithm for extracting keypoints and computing descriptors. ' help=('Choose the algorithm for extracting keypoints and computing descriptors. '
'Can be one of: %(choices)s. Default: ' 'Can be one of: %(choices)s. Default: '
@ -485,7 +485,7 @@ def config(argv=None, parser=None):
metavar='<positive float>', metavar='<positive float>',
action=StoreValue, action=StoreValue,
type=float, type=float,
default=2.5, 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. ' 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') 'Default: %(default)s')
@ -838,7 +838,7 @@ def config(argv=None, parser=None):
type=float, type=float,
action=StoreValue, action=StoreValue,
metavar='<positive float>', metavar='<positive float>',
default=10, default=3,
help='Set a value in meters for the GPS Dilution of Precision (DOP) ' help='Set a value in meters for the GPS Dilution of Precision (DOP) '
'information for all images. If your images are tagged ' 'information for all images. If your images are tagged '
'with high precision GPS information (RTK), this value will be automatically ' 'with high precision GPS information (RTK), this value will be automatically '

Wyświetl plik

@ -12,6 +12,9 @@ class GeoFile:
with open(self.geo_path, 'r') as f: with open(self.geo_path, 'r') as f:
contents = f.read().strip() contents = f.read().strip()
# Strip eventual BOM characters
contents = contents.replace('\ufeff', '')
lines = list(map(str.strip, contents.split('\n'))) lines = list(map(str.strip, contents.split('\n')))
if lines: if lines:

Wyświetl plik

@ -279,9 +279,10 @@ def obj2glb(input_obj, output_glb, rtc=(None, None), draco_compression=True, _in
) )
gltf.extensionsRequired = ['KHR_materials_unlit'] gltf.extensionsRequired = ['KHR_materials_unlit']
gltf.extensionsUsed = ['KHR_materials_unlit']
if rtc != (None, None) and len(rtc) >= 2: if rtc != (None, None) and len(rtc) >= 2:
gltf.extensionsUsed = ['CESIUM_RTC', 'KHR_materials_unlit'] gltf.extensionsUsed.append('CESIUM_RTC')
gltf.extensions = { gltf.extensions = {
'CESIUM_RTC': { 'CESIUM_RTC': {
'center': [float(rtc[0]), float(rtc[1]), 0.0] 'center': [float(rtc[0]), float(rtc[1]), 0.0]

Wyświetl plik

@ -187,7 +187,7 @@ def screened_poisson_reconstruction(inPointCloud, outMesh, depth = 8, samples =
if threads < 1: if threads < 1:
break break
else: else:
log.ODM_WARNING("PoissonRecon failed with %s threads, let's retry with %s..." % (threads, threads // 2)) log.ODM_WARNING("PoissonRecon failed with %s threads, let's retry with %s..." % (threads * 2, threads))
# Cleanup and reduce vertex count if necessary # Cleanup and reduce vertex count if necessary

Wyświetl plik

@ -64,7 +64,6 @@ class OSFMContext:
"Check that the images have enough overlap, " "Check that the images have enough overlap, "
"that there are enough recognizable features " "that there are enough recognizable features "
"and that the images are in focus. " "and that the images are in focus. "
"You could also try to increase the --min-num-features parameter."
"The program will now exit.") "The program will now exit.")
if rolling_shutter_correct: if rolling_shutter_correct:
@ -794,3 +793,12 @@ def get_all_submodel_paths(submodels_path, *all_paths):
result.append([os.path.join(submodels_path, f, ap) for ap in all_paths]) result.append([os.path.join(submodels_path, f, ap) for ap in all_paths])
return result return result
def is_submodel(opensfm_root):
# A bit hackish, but works without introducing additional markers / flags
# Look at the path of the opensfm directory and see if "submodel_" is part of it
parts = os.path.abspath(opensfm_root).split(os.path.sep)
return (len(parts) >= 2 and parts[-2][:9] == "submodel_") or \
os.path.isfile(os.path.join(opensfm_root, "split_merge_stop_at_reconstruction.txt")) or \
os.path.isfile(os.path.join(opensfm_root, "features", "empty"))

Wyświetl plik

@ -430,7 +430,8 @@ class ODM_Photo:
camera_projection = camera_projection.lower() camera_projection = camera_projection.lower()
# Parrot Sequoia's "fisheye" model maps to "fisheye_opencv" # Parrot Sequoia's "fisheye" model maps to "fisheye_opencv"
if camera_projection == "fisheye" and self.camera_make.lower() == "parrot" and self.camera_model.lower() == "sequoia": # or better yet, replace all fisheye with fisheye_opencv, but wait to change API signature
if camera_projection == "fisheye":
camera_projection = "fisheye_opencv" camera_projection = "fisheye_opencv"
if camera_projection in projections: if camera_projection in projections:

Wyświetl plik

@ -9,6 +9,8 @@ from opendm.concurrency import parallel_map
from opendm.utils import double_quote from opendm.utils import double_quote
from opendm.boundary import as_polygon, as_geojson from opendm.boundary import as_polygon, as_geojson
from opendm.dem.pdal import run_pipeline from opendm.dem.pdal import run_pipeline
from opendm.opc import classify
from opendm.dem import commands
def ply_info(input_ply): def ply_info(input_ply):
if not os.path.exists(input_ply): if not os.path.exists(input_ply):
@ -274,6 +276,32 @@ def merge_ply(input_point_cloud_files, output_file, dims=None):
system.run(' '.join(cmd)) system.run(' '.join(cmd))
def post_point_cloud_steps(args, tree, rerun=False): def post_point_cloud_steps(args, tree, rerun=False):
# Classify and rectify before generating derivate files
if args.pc_classify:
pc_classify_marker = os.path.join(tree.odm_georeferencing, 'pc_classify_done.txt')
if not io.file_exists(pc_classify_marker) or rerun:
log.ODM_INFO("Classifying {} using Simple Morphological Filter (1/2)".format(tree.odm_georeferencing_model_laz))
commands.classify(tree.odm_georeferencing_model_laz,
args.smrf_scalar,
args.smrf_slope,
args.smrf_threshold,
args.smrf_window
)
log.ODM_INFO("Classifying {} using OpenPointClass (2/2)".format(tree.odm_georeferencing_model_laz))
classify(tree.odm_georeferencing_model_laz, args.max_concurrency)
with open(pc_classify_marker, 'w') as f:
f.write('Classify: smrf\n')
f.write('Scalar: {}\n'.format(args.smrf_scalar))
f.write('Slope: {}\n'.format(args.smrf_slope))
f.write('Threshold: {}\n'.format(args.smrf_threshold))
f.write('Window: {}\n'.format(args.smrf_window))
if args.pc_rectify:
commands.rectify(tree.odm_georeferencing_model_laz)
# XYZ point cloud output # XYZ point cloud output
if args.pc_csv: if args.pc_csv:
log.ODM_INFO("Creating CSV file (XYZ format)") log.ODM_INFO("Creating CSV file (XYZ format)")

Wyświetl plik

@ -19,6 +19,7 @@ RS_DATABASE = {
'dji fc220': 64, # DJI Mavic Pro (Platinum) 'dji fc220': 64, # DJI Mavic Pro (Platinum)
'hasselblad l1d-20c': lambda p: 47 if p.get_capture_megapixels() < 17 else 56, # DJI Mavic 2 Pro (at 16:10 => 16.8MP 47ms, at 3:2 => 19.9MP 56ms. 4:3 has 17.7MP with same image height as 3:2 which can be concluded as same sensor readout) 'hasselblad l1d-20c': lambda p: 47 if p.get_capture_megapixels() < 17 else 56, # DJI Mavic 2 Pro (at 16:10 => 16.8MP 47ms, at 3:2 => 19.9MP 56ms. 4:3 has 17.7MP with same image height as 3:2 which can be concluded as same sensor readout)
'hasselblad l2d-20c': 16.6, # DJI Mavic 3 (not enterprise version)
'dji fc3582': lambda p: 26 if p.get_capture_megapixels() < 48 else 60, # DJI Mini 3 pro (at 48MP readout is 60ms, at 12MP it's 26ms) 'dji fc3582': lambda p: 26 if p.get_capture_megapixels() < 48 else 60, # DJI Mini 3 pro (at 48MP readout is 60ms, at 12MP it's 26ms)

Wyświetl plik

@ -13,6 +13,7 @@ from opendm import log
from opendm import io from opendm import io
from opendm import system from opendm import system
from opendm import context from opendm import context
from opendm import multispectral
from opendm.progress import progressbc from opendm.progress import progressbc
from opendm.photo import ODM_Photo from opendm.photo import ODM_Photo
@ -45,19 +46,51 @@ class ODM_Reconstruction(object):
band_photos[p.band_name].append(p) band_photos[p.band_name].append(p)
bands_count = len(band_photos) bands_count = len(band_photos)
if bands_count >= 2 and bands_count <= 8:
# Band name with the minimum number of photos
max_band_name = None
max_photos = -1
for band_name in band_photos:
if len(band_photos[band_name]) > max_photos:
max_band_name = band_name
max_photos = len(band_photos[band_name])
if bands_count >= 2 and bands_count <= 10:
# Validate that all bands have the same number of images, # Validate that all bands have the same number of images,
# otherwise this is not a multi-camera setup # otherwise this is not a multi-camera setup
img_per_band = len(band_photos[p.band_name]) img_per_band = len(band_photos[max_band_name])
for band in band_photos:
if len(band_photos[band]) != img_per_band:
log.ODM_ERROR("Multi-camera setup detected, but band \"%s\" (identified from \"%s\") has only %s images (instead of %s), perhaps images are missing or are corrupted. Please include all necessary files to process all bands and try again." % (band, band_photos[band][0].filename, len(band_photos[band]), img_per_band))
raise RuntimeError("Invalid multi-camera images")
mc = [] mc = []
for band_name in band_indexes: for band_name in band_indexes:
mc.append({'name': band_name, 'photos': band_photos[band_name]}) mc.append({'name': band_name, 'photos': band_photos[band_name]})
filter_missing = False
for band in band_photos:
if len(band_photos[band]) < img_per_band:
log.ODM_WARNING("Multi-camera setup detected, but band \"%s\" (identified from \"%s\") has only %s images (instead of %s), perhaps images are missing or are corrupted." % (band, band_photos[band][0].filename, len(band_photos[band]), len(band_photos[max_band_name])))
filter_missing = True
if filter_missing:
# Calculate files to ignore
_, p2s = multispectral.compute_band_maps(mc, max_band_name)
max_files_per_band = 0
for filename in p2s:
max_files_per_band = max(max_files_per_band, len(p2s[filename]))
for filename in p2s:
if len(p2s[filename]) < max_files_per_band:
photos_to_remove = p2s[filename] + [p for p in self.photos if p.filename == filename]
for photo in photos_to_remove:
log.ODM_WARNING("Excluding %s" % photo.filename)
self.photos = [p for p in self.photos if p != photo]
for i in range(len(mc)):
mc[i]['photos'] = [p for p in mc[i]['photos'] if p != photo]
log.ODM_INFO("New image count: %s" % len(self.photos))
# We enforce a normalized band order for all bands that we can identify # We enforce a normalized band order for all bands that we can identify
# and rely on the manufacturer's band_indexes as a fallback for all others # and rely on the manufacturer's band_indexes as a fallback for all others
normalized_band_order = { normalized_band_order = {
@ -94,7 +127,7 @@ class ODM_Reconstruction(object):
for c, d in enumerate(mc): for c, d in enumerate(mc):
log.ODM_INFO(f"Band {c + 1}: {d['name']}") log.ODM_INFO(f"Band {c + 1}: {d['name']}")
return mc return mc
return None return None

Wyświetl plik

@ -54,8 +54,10 @@ class SrtFileParser:
if not self.gps_data: if not self.gps_data:
for d in self.data: for d in self.data:
lat, lon, alt = d.get('latitude'), d.get('longitude'), d.get('altitude') lat, lon, alt = d.get('latitude'), d.get('longitude'), d.get('altitude')
if alt is None:
alt = 0
tm = d.get('start') tm = d.get('start')
if lat is not None and lon is not None: if lat is not None and lon is not None:
if self.ll_to_utm is None: if self.ll_to_utm is None:
self.ll_to_utm, self.utm_to_ll = location.utm_transformers_from_ll(lon, lat) self.ll_to_utm, self.utm_to_ll = location.utm_transformers_from_ll(lon, lat)
@ -127,6 +129,20 @@ class SrtFileParser:
# 00:00:35,000 --> 00:00:36,000 # 00:00:35,000 --> 00:00:36,000
# F/6.3, SS 60, ISO 100, EV 0, RTK (120.083799, 30.213635, 28), HOME (120.084146, 30.214243, 103.55m), D 75.36m, H 76.19m, H.S 0.30m/s, V.S 0.00m/s, F.PRY (-5.3°, 2.1°, 28.3°), G.PRY (-40.0°, 0.0°, 28.2°) # F/6.3, SS 60, ISO 100, EV 0, RTK (120.083799, 30.213635, 28), HOME (120.084146, 30.214243, 103.55m), D 75.36m, H 76.19m, H.S 0.30m/s, V.S 0.00m/s, F.PRY (-5.3°, 2.1°, 28.3°), G.PRY (-40.0°, 0.0°, 28.2°)
# DJI Unknown Model #1
# 1
# 00:00:00,000 --> 00:00:00,033
# <font size="28">SrtCnt : 1, DiffTime : 33ms
# 2024-01-18 10:23:26.397
# [iso : 150] [shutter : 1/5000.0] [fnum : 170] [ev : 0] [ct : 5023] [color_md : default] [focal_len : 240] [dzoom_ratio: 10000, delta:0],[latitude: -22.724555] [longitude: -47.602414] [rel_alt: 0.300 abs_alt: 549.679] </font>
# DJI Mavic 2 Zoom
# 1
# 00:00:00,000 --> 00:00:00,041
# <font size="36">FrameCnt : 1, DiffTime : 41ms
# 2023-07-15 11:55:16,320,933
# [iso : 100] [shutter : 1/400.0] [fnum : 280] [ev : 0] [ct : 5818] [color_md : default] [focal_len : 240] [latitude : 0.000000] [longtitude : 0.000000] [altitude: 0.000000] </font>
with open(self.filename, 'r') as f: with open(self.filename, 'r') as f:
iso = None iso = None
@ -197,12 +213,14 @@ class SrtFileParser:
latitude = match_single([ latitude = match_single([
("latitude: ([\d\.\-]+)", lambda v: float(v) if v != 0 else None), ("latitude: ([\d\.\-]+)", lambda v: float(v) if v != 0 else None),
("latitude : ([\d\.\-]+)", lambda v: float(v) if v != 0 else None),
("GPS \([\d\.\-]+,? ([\d\.\-]+),? [\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), ("RTK \([-+]?\d+\.\d+, (-?\d+\.\d+), -?\d+\)", lambda v: float(v) if v != 0 else None),
], line) ], line)
longitude = match_single([ longitude = match_single([
("longitude: ([\d\.\-]+)", lambda v: float(v) if v != 0 else None), ("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), ("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), ("RTK \((-?\d+\.\d+), [-+]?\d+\.\d+, -?\d+\)", lambda v: float(v) if v != 0 else None),
], line) ], line)
@ -211,4 +229,5 @@ class SrtFileParser:
("altitude: ([\d\.\-]+)", lambda v: float(v) if v != 0 else None), ("altitude: ([\d\.\-]+)", lambda v: float(v) if v != 0 else None),
("GPS \([\d\.\-]+,? [\d\.\-]+,? ([\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), ("RTK \([-+]?\d+\.\d+, [-+]?\d+\.\d+, (-?\d+)\)", lambda v: float(v) if v != 0 else None),
("abs_alt: ([\d\.\-]+)", lambda v: float(v) if v != 0 else None),
], line) ], line)

Wyświetl plik

@ -110,7 +110,7 @@ class ODMMvsTexStage(types.ODM_Stage):
mvs_tmp_dir = os.path.join(r['out_dir'], 'tmp') mvs_tmp_dir = os.path.join(r['out_dir'], 'tmp')
# Make sure tmp directory is empty # mvstex creates a tmp directory, so make sure it is empty
if io.dir_exists(mvs_tmp_dir): if io.dir_exists(mvs_tmp_dir):
log.ODM_INFO("Removing old tmp directory {}".format(mvs_tmp_dir)) log.ODM_INFO("Removing old tmp directory {}".format(mvs_tmp_dir))
shutil.rmtree(mvs_tmp_dir) shutil.rmtree(mvs_tmp_dir)

Wyświetl plik

@ -12,7 +12,6 @@ from opendm.cropper import Cropper
from opendm import pseudogeo from opendm import pseudogeo
from opendm.tiles.tiler import generate_dem_tiles from opendm.tiles.tiler import generate_dem_tiles
from opendm.cogeo import convert_to_cogeo from opendm.cogeo import convert_to_cogeo
from opendm.opc import classify
class ODMDEMStage(types.ODM_Stage): class ODMDEMStage(types.ODM_Stage):
def process(self, args, outputs): def process(self, args, outputs):
@ -35,7 +34,6 @@ class ODMDEMStage(types.ODM_Stage):
ignore_resolution=ignore_resolution and args.ignore_gsd, ignore_resolution=ignore_resolution and args.ignore_gsd,
has_gcp=reconstruction.has_gcp()) has_gcp=reconstruction.has_gcp())
log.ODM_INFO('Classify: ' + str(args.pc_classify))
log.ODM_INFO('Create DSM: ' + str(args.dsm)) log.ODM_INFO('Create DSM: ' + str(args.dsm))
log.ODM_INFO('Create DTM: ' + str(args.dtm)) log.ODM_INFO('Create DTM: ' + str(args.dtm))
log.ODM_INFO('DEM input file {0} found: {1}'.format(dem_input, str(pc_model_found))) log.ODM_INFO('DEM input file {0} found: {1}'.format(dem_input, str(pc_model_found)))
@ -45,34 +43,9 @@ class ODMDEMStage(types.ODM_Stage):
if not io.dir_exists(odm_dem_root): if not io.dir_exists(odm_dem_root):
system.mkdir_p(odm_dem_root) system.mkdir_p(odm_dem_root)
if args.pc_classify and pc_model_found:
pc_classify_marker = os.path.join(odm_dem_root, 'pc_classify_done.txt')
if not io.file_exists(pc_classify_marker) or self.rerun():
log.ODM_INFO("Classifying {} using Simple Morphological Filter (1/2)".format(dem_input))
commands.classify(dem_input,
args.smrf_scalar,
args.smrf_slope,
args.smrf_threshold,
args.smrf_window
)
log.ODM_INFO("Classifying {} using OpenPointClass (2/2)".format(dem_input))
classify(dem_input, args.max_concurrency)
with open(pc_classify_marker, 'w') as f:
f.write('Classify: smrf\n')
f.write('Scalar: {}\n'.format(args.smrf_scalar))
f.write('Slope: {}\n'.format(args.smrf_slope))
f.write('Threshold: {}\n'.format(args.smrf_threshold))
f.write('Window: {}\n'.format(args.smrf_window))
progress = 20 progress = 20
self.update_progress(progress) self.update_progress(progress)
if args.pc_rectify:
commands.rectify(dem_input)
# Do we need to process anything here? # Do we need to process anything here?
if (args.dsm or args.dtm) and pc_model_found: if (args.dsm or args.dtm) and pc_model_found:
dsm_output_filename = os.path.join(odm_dem_root, 'dsm.tif') dsm_output_filename = os.path.join(odm_dem_root, 'dsm.tif')
@ -120,7 +93,7 @@ class ODMDEMStage(types.ODM_Stage):
if args.cog: if args.cog:
convert_to_cogeo(dem_geotiff_path, max_workers=args.max_concurrency) convert_to_cogeo(dem_geotiff_path, max_workers=args.max_concurrency)
progress += 30 progress += 40
self.update_progress(progress) self.update_progress(progress)
else: else:
log.ODM_WARNING('Found existing outputs in: %s' % odm_dem_root) log.ODM_WARNING('Found existing outputs in: %s' % odm_dem_root)

Wyświetl plik

@ -7,6 +7,7 @@ from opendm import context
from opendm import types from opendm import types
from opendm import gsd from opendm import gsd
from opendm import orthophoto from opendm import orthophoto
from opendm.osfm import is_submodel
from opendm.concurrency import get_max_memory_mb from opendm.concurrency import get_max_memory_mb
from opendm.cutline import compute_cutline from opendm.cutline import compute_cutline
from opendm.utils import double_quote from opendm.utils import double_quote
@ -114,6 +115,7 @@ class ODMOrthoPhotoStage(types.ODM_Stage):
# Cutline computation, before cropping # Cutline computation, before cropping
# We want to use the full orthophoto, not the cropped one. # We want to use the full orthophoto, not the cropped one.
submodel_run = is_submodel(tree.opensfm)
if args.orthophoto_cutline: if args.orthophoto_cutline:
cutline_file = os.path.join(tree.odm_orthophoto, "cutline.gpkg") cutline_file = os.path.join(tree.odm_orthophoto, "cutline.gpkg")
@ -122,15 +124,18 @@ class ODMOrthoPhotoStage(types.ODM_Stage):
cutline_file, cutline_file,
args.max_concurrency, args.max_concurrency,
scale=0.25) scale=0.25)
orthophoto.compute_mask_raster(tree.odm_orthophoto_tif, cutline_file, if submodel_run:
os.path.join(tree.odm_orthophoto, "odm_orthophoto_cut.tif"), orthophoto.compute_mask_raster(tree.odm_orthophoto_tif, cutline_file,
blend_distance=20, only_max_coords_feature=True) os.path.join(tree.odm_orthophoto, "odm_orthophoto_cut.tif"),
blend_distance=20, only_max_coords_feature=True)
else:
log.ODM_INFO("Not a submodel run, skipping mask raster generation")
orthophoto.post_orthophoto_steps(args, bounds_file_path, tree.odm_orthophoto_tif, tree.orthophoto_tiles, resolution) orthophoto.post_orthophoto_steps(args, bounds_file_path, tree.odm_orthophoto_tif, tree.orthophoto_tiles, resolution)
# Generate feathered orthophoto also # Generate feathered orthophoto also
if args.orthophoto_cutline: if args.orthophoto_cutline and submodel_run:
orthophoto.feather_raster(tree.odm_orthophoto_tif, orthophoto.feather_raster(tree.odm_orthophoto_tif,
os.path.join(tree.odm_orthophoto, "odm_orthophoto_feathered.tif"), os.path.join(tree.odm_orthophoto, "odm_orthophoto_feathered.tif"),
blend_distance=20 blend_distance=20

Wyświetl plik

@ -94,7 +94,7 @@ class ODMOpenMVSStage(types.ODM_Stage):
extra_config.append("--ignore-mask-label 0") extra_config.append("--ignore-mask-label 0")
with open(densify_ini_file, 'w+') as f: with open(densify_ini_file, 'w+') as f:
f.write("Optimize = 7\n") f.write("Optimize = 7\nMin Views Filter = 1\n")
def run_densify(): def run_densify():
system.run('"%s" "%s" %s' % (context.omvs_densify_path, system.run('"%s" "%s" %s' % (context.omvs_densify_path,
@ -110,7 +110,7 @@ class ODMOpenMVSStage(types.ODM_Stage):
log.ODM_WARNING("OpenMVS failed with GPU, is your graphics card driver up to date? Falling back to CPU.") log.ODM_WARNING("OpenMVS failed with GPU, is your graphics card driver up to date? Falling back to CPU.")
gpu_config = ["--cuda-device -2"] gpu_config = ["--cuda-device -2"]
run_densify() run_densify()
elif (e.errorCode == 137 or e.errorCode == 3221226505) and not pc_tile: elif (e.errorCode == 137 or e.errorCode == 143 or e.errorCode == 3221226505) and not pc_tile:
log.ODM_WARNING("OpenMVS ran out of memory, we're going to turn on tiling to see if we can process this.") log.ODM_WARNING("OpenMVS ran out of memory, we're going to turn on tiling to see if we can process this.")
pc_tile = True pc_tile = True
config.append("--fusion-mode 1") config.append("--fusion-mode 1")
@ -127,7 +127,7 @@ class ODMOpenMVSStage(types.ODM_Stage):
subscene_densify_ini_file = os.path.join(tree.openmvs, 'subscene-config.ini') subscene_densify_ini_file = os.path.join(tree.openmvs, 'subscene-config.ini')
with open(subscene_densify_ini_file, 'w+') as f: with open(subscene_densify_ini_file, 'w+') as f:
f.write("Optimize = 0\nEstimation Geometric Iters = 0\n") f.write("Optimize = 0\nEstimation Geometric Iters = 0\nMin Views Filter = 1\n")
config = [ config = [
"--sub-scene-area 660000", # 8000 "--sub-scene-area 660000", # 8000
@ -223,7 +223,7 @@ class ODMOpenMVSStage(types.ODM_Stage):
try: try:
system.run('"%s" %s' % (context.omvs_densify_path, ' '.join(config + gpu_config + extra_config))) system.run('"%s" %s' % (context.omvs_densify_path, ' '.join(config + gpu_config + extra_config)))
except system.SubprocessException as e: except system.SubprocessException as e:
if e.errorCode == 137 or e.errorCode == 3221226505: if e.errorCode == 137 or e.errorCode == 143 or e.errorCode == 3221226505:
log.ODM_WARNING("OpenMVS filtering ran out of memory, visibility checks will be skipped.") log.ODM_WARNING("OpenMVS filtering ran out of memory, visibility checks will be skipped.")
skip_filtering() skip_filtering()
else: else: