pingmapper 5.4.9__tar.gz → 5.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pingmapper-5.4.9 → pingmapper-5.5.0}/PKG-INFO +1 -1
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/default_params.json +8 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/gui_main.py +92 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/main_readFiles.py +616 -0
- pingmapper-5.5.0/pingmapper/version.py +1 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper.egg-info/PKG-INFO +1 -1
- pingmapper-5.4.9/pingmapper/version.py +0 -1
- {pingmapper-5.4.9 → pingmapper-5.5.0}/LICENSE +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/README.md +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/__init__.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/__main__.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/class_mapSubstrateObj.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/class_portstarObj.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/class_rectObj.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/class_sonObj.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/class_sonObj_nadirgaptest.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/doWork.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/funcs_common.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/funcs_model.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/funcs_rectify.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/main_mapSubstrate.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/main_rectify.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/nonGUI_batch_main.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/nonGui_main.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/processing_scripts/main_batchDirectory_2024-01-18_0926.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/processing_scripts/main_batchDirectory_2024-01-18_0929.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/scratch/funcs_pyhum_correct.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/scratch/main.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/scratch/main_batchDirectory.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/self_check.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/test_PINGMapper.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/test_cli_self_check.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/test_dq_filter.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/test_time.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/DRAFT_Workflows/avg_predictions_Mussel_WBL.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/DRAFT_Workflows/gen_centerline.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/DRAFT_Workflows/gen_centerline_from_bankline.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/DRAFT_Workflows/gen_centerline_trkpnts_fitspline_DRAFT.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/DRAFT_Workflows/testEXAMPLE_mosaic_logit.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/RawEGN_avg_predictions.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/Substrate_Summaries/00_substrate_logits_mosaic_transects.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/Substrate_Summaries/00_substrate_shps_mosaic_transects.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/Substrate_Summaries/01_gen_centerline_from_coverage.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/Substrate_Summaries/02_gen_summary_stamp_shps.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/Substrate_Summaries/03_gen_summary_shp.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/Substrate_Summaries/04_combine_summary_shp_csv.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/Substrate_Summaries/05_gen_summary_shp_plots.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/Substrate_Summaries/06_compare_raw-egn_volume.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/Substrate_Summaries/08_raw-egn_hardReacheFreq_hist.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/Substrate_Summaries/09_raw-egn_PatchSize_density.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/Substrate_Summaries/summarize_project_substrate.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/export_coverage.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/main_mosaic_transects.py +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper.egg-info/SOURCES.txt +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper.egg-info/dependency_links.txt +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper.egg-info/requires.txt +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper.egg-info/top_level.txt +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/pyproject.toml +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/setup.cfg +0 -0
- {pingmapper-5.4.9 → pingmapper-5.5.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pingmapper
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.5.0
|
|
4
4
|
Summary: Open-source interface for processing recreation-grade side scan sonar datasets and reproducibly mapping benthic habitat
|
|
5
5
|
Author: Cameron Bodine, Daniel Buscombe, Iury T Simoes-Sousa
|
|
6
6
|
Author-email: bodine.cs@gmail.email
|
|
@@ -29,6 +29,14 @@
|
|
|
29
29
|
"wcm":true,
|
|
30
30
|
"wcr":false,
|
|
31
31
|
"wco":false,
|
|
32
|
+
"waterfall_ss_image":false,
|
|
33
|
+
"waterfall_ss_video":false,
|
|
34
|
+
"waterfall_di_image":false,
|
|
35
|
+
"waterfall_di_video":false,
|
|
36
|
+
"waterfall_video_fps":10,
|
|
37
|
+
"waterfall_video_resolution":"1080p",
|
|
38
|
+
"waterfall_mode_selection":"Auto (enabled exports)",
|
|
39
|
+
"waterfall_window_stride":64,
|
|
32
40
|
"export_16bit":false,
|
|
33
41
|
"export_colormap_uint8":true,
|
|
34
42
|
"tileFile":".jpg",
|
|
@@ -382,6 +382,84 @@ def gui(batch: bool):
|
|
|
382
382
|
layout.append([text_tile])
|
|
383
383
|
layout.append([col_tile_1, sg.VerticalSeparator(), col_tile_2, sg.VerticalSeparator(), col_tile_3])
|
|
384
384
|
|
|
385
|
+
############################
|
|
386
|
+
# Waterfall Image/Video Exports
|
|
387
|
+
|
|
388
|
+
text_waterfall = sg.Text('Waterfall Image/Video Exports\n', font=("Helvetica", 14, "underline"))
|
|
389
|
+
|
|
390
|
+
tip_wf_ss_img = ml_tip('Export combined side-scan waterfall image (port+star), arranged for top-to-bottom scrolling.')
|
|
391
|
+
tip_wf_ss_vid = ml_tip('Export side-scan scrolling video (top to bottom) from combined port+star waterfall.')
|
|
392
|
+
tip_wf_di_img = ml_tip('Export down-imaging waterfall image for each down-looking beam.')
|
|
393
|
+
tip_wf_di_vid = ml_tip('Export down-imaging scrolling video (right to left) for each down-looking beam.')
|
|
394
|
+
tip_wf_fps = ml_tip('Waterfall video frame rate (frames per second).')
|
|
395
|
+
tip_wf_res = ml_tip('Video output resolution preset for waterfall videos.')
|
|
396
|
+
tip_wf_mode = ml_tip('Tile mode(s) used for waterfall export. Auto uses currently enabled tile exports.')
|
|
397
|
+
tip_wf_stride = ml_tip('Window step in pixels per frame (larger = faster scrolling).')
|
|
398
|
+
|
|
399
|
+
check_wf_ss_img = sg.Checkbox(
|
|
400
|
+
'Side-scan Waterfall Image (Port+Star Combined)',
|
|
401
|
+
key='waterfall_ss_image',
|
|
402
|
+
default=default_params.get('waterfall_ss_image', False),
|
|
403
|
+
tooltip=tip_wf_ss_img,
|
|
404
|
+
)
|
|
405
|
+
check_wf_ss_vid = sg.Checkbox(
|
|
406
|
+
'Side-scan Waterfall Video (Top -> Bottom)',
|
|
407
|
+
key='waterfall_ss_video',
|
|
408
|
+
default=default_params.get('waterfall_ss_video', False),
|
|
409
|
+
tooltip=tip_wf_ss_vid,
|
|
410
|
+
)
|
|
411
|
+
check_wf_di_img = sg.Checkbox(
|
|
412
|
+
'Down-imaging Waterfall Image',
|
|
413
|
+
key='waterfall_di_image',
|
|
414
|
+
default=default_params.get('waterfall_di_image', False),
|
|
415
|
+
tooltip=tip_wf_di_img,
|
|
416
|
+
)
|
|
417
|
+
check_wf_di_vid = sg.Checkbox(
|
|
418
|
+
'Down-imaging Waterfall Video (Right -> Left)',
|
|
419
|
+
key='waterfall_di_video',
|
|
420
|
+
default=default_params.get('waterfall_di_video', False),
|
|
421
|
+
tooltip=tip_wf_di_vid,
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
text_wf_fps = sg.Text('Video FPS', size=(20,1))
|
|
425
|
+
in_wf_fps = sg.Input(
|
|
426
|
+
key='waterfall_video_fps',
|
|
427
|
+
default_text=default_params.get('waterfall_video_fps', 10),
|
|
428
|
+
size=(10,1),
|
|
429
|
+
tooltip=tip_wf_fps,
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
text_wf_res = sg.Text('Video Resolution', size=(20,1))
|
|
433
|
+
combo_wf_res = sg.Combo(
|
|
434
|
+
['4K', '1080p', '720p', '4xxp'],
|
|
435
|
+
key='waterfall_video_resolution',
|
|
436
|
+
default_value=default_params.get('waterfall_video_resolution', '1080p'),
|
|
437
|
+
tooltip=tip_wf_res,
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
text_wf_mode = sg.Text('Waterfall Mode(s)', size=(20,1))
|
|
441
|
+
combo_wf_mode = sg.Combo(
|
|
442
|
+
['Auto (enabled exports)', 'WCP', 'SRC', 'WCP+SRC'],
|
|
443
|
+
key='waterfall_mode_selection',
|
|
444
|
+
default_value=default_params.get('waterfall_mode_selection', 'Auto (enabled exports)'),
|
|
445
|
+
tooltip=tip_wf_mode,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
text_wf_stride = sg.Text('Window Stride [px]', size=(20,1))
|
|
449
|
+
in_wf_stride = sg.Input(
|
|
450
|
+
key='waterfall_window_stride',
|
|
451
|
+
default_text=default_params.get('waterfall_window_stride', 64),
|
|
452
|
+
size=(10,1),
|
|
453
|
+
tooltip=tip_wf_stride,
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
col_wf_1 = sg.Column([[check_wf_ss_img], [check_wf_ss_vid], [check_wf_di_img], [check_wf_di_vid]], pad=0)
|
|
457
|
+
col_wf_2 = sg.Column([[text_wf_fps, in_wf_fps], [text_wf_res, combo_wf_res], [text_wf_mode, combo_wf_mode], [text_wf_stride, in_wf_stride]], pad=0)
|
|
458
|
+
|
|
459
|
+
layout.append([sg.HorizontalSeparator()])
|
|
460
|
+
layout.append([text_waterfall])
|
|
461
|
+
layout.append([col_wf_1, sg.VerticalSeparator(), col_wf_2])
|
|
462
|
+
|
|
385
463
|
########################
|
|
386
464
|
# Depth & Shadow Removal
|
|
387
465
|
|
|
@@ -695,6 +773,12 @@ def gui(batch: bool):
|
|
|
695
773
|
map_mosaic = int(map_mosaic)
|
|
696
774
|
|
|
697
775
|
|
|
776
|
+
wf_mode = values['waterfall_mode_selection']
|
|
777
|
+
if wf_mode == 'Auto (enabled exports)':
|
|
778
|
+
wf_mode = 'auto'
|
|
779
|
+
else:
|
|
780
|
+
wf_mode = str(wf_mode).strip().lower()
|
|
781
|
+
|
|
698
782
|
params = {
|
|
699
783
|
# 'humFile':values[0],
|
|
700
784
|
# 'projDir':os.path.join(values[1], values[2]),
|
|
@@ -753,6 +837,14 @@ def gui(batch: bool):
|
|
|
753
837
|
'map_mosaic':map_mosaic,
|
|
754
838
|
'banklines':values['banklines'],
|
|
755
839
|
'coverage':values['coverage']
|
|
840
|
+
, 'waterfall_ss_image':values['waterfall_ss_image']
|
|
841
|
+
, 'waterfall_ss_video':values['waterfall_ss_video']
|
|
842
|
+
, 'waterfall_di_image':values['waterfall_di_image']
|
|
843
|
+
, 'waterfall_di_video':values['waterfall_di_video']
|
|
844
|
+
, 'waterfall_video_fps':int(values['waterfall_video_fps'])
|
|
845
|
+
, 'waterfall_video_resolution':values['waterfall_video_resolution']
|
|
846
|
+
, 'waterfall_mode_selection':wf_mode
|
|
847
|
+
, 'waterfall_window_stride':int(values['waterfall_window_stride'])
|
|
756
848
|
}
|
|
757
849
|
|
|
758
850
|
globals().update(params)
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
# SOFTWARE.
|
|
31
31
|
|
|
32
32
|
import os, sys
|
|
33
|
+
import re
|
|
33
34
|
|
|
34
35
|
# Add 'pingmapper' to the path, may not need after pypi package...
|
|
35
36
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
@@ -105,6 +106,576 @@ def _sidescan_group_key(beam_name):
|
|
|
105
106
|
return beam_name
|
|
106
107
|
|
|
107
108
|
|
|
109
|
+
def _extract_chunk_id_from_path(path):
|
|
110
|
+
name = os.path.splitext(os.path.basename(path))[0]
|
|
111
|
+
match = re.search(r'_(\d+)$', name)
|
|
112
|
+
if match:
|
|
113
|
+
return int(match.group(1))
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _sort_tile_paths(paths):
|
|
118
|
+
def _key(path):
|
|
119
|
+
chunk_id = _extract_chunk_id_from_path(path)
|
|
120
|
+
if chunk_id is None:
|
|
121
|
+
return (1, os.path.basename(path))
|
|
122
|
+
return (0, chunk_id)
|
|
123
|
+
|
|
124
|
+
return sorted(paths, key=_key)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _to_bgr8(img):
|
|
128
|
+
if img is None:
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
if img.dtype == np.uint16:
|
|
132
|
+
img = (img / 257.0).astype(np.uint8)
|
|
133
|
+
elif img.dtype != np.uint8:
|
|
134
|
+
arr = np.asarray(img, dtype=np.float32)
|
|
135
|
+
finite = np.isfinite(arr)
|
|
136
|
+
if finite.any():
|
|
137
|
+
lo = arr[finite].min()
|
|
138
|
+
hi = arr[finite].max()
|
|
139
|
+
if hi > lo:
|
|
140
|
+
arr = (arr - lo) / (hi - lo)
|
|
141
|
+
else:
|
|
142
|
+
arr = np.zeros_like(arr)
|
|
143
|
+
else:
|
|
144
|
+
arr = np.zeros_like(arr)
|
|
145
|
+
img = (arr * 255.0).clip(0, 255).astype(np.uint8)
|
|
146
|
+
|
|
147
|
+
if img.ndim == 2:
|
|
148
|
+
return cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
|
|
149
|
+
|
|
150
|
+
if img.ndim == 3 and img.shape[2] == 4:
|
|
151
|
+
return cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
|
|
152
|
+
|
|
153
|
+
return img
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _resize_height(img, target_h):
|
|
157
|
+
if img.shape[0] == target_h:
|
|
158
|
+
return img
|
|
159
|
+
|
|
160
|
+
h, w = img.shape[:2]
|
|
161
|
+
if h <= 0 or w <= 0 or target_h <= 0:
|
|
162
|
+
return img
|
|
163
|
+
|
|
164
|
+
new_w = max(1, int(round(w * (target_h / float(h)))))
|
|
165
|
+
return cv2.resize(img, (new_w, target_h), interpolation=cv2.INTER_AREA)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _resize_width(img, target_w):
|
|
169
|
+
if img.shape[1] == target_w:
|
|
170
|
+
return img
|
|
171
|
+
|
|
172
|
+
h, w = img.shape[:2]
|
|
173
|
+
if h <= 0 or w <= 0 or target_w <= 0:
|
|
174
|
+
return img
|
|
175
|
+
|
|
176
|
+
new_h = max(1, int(round(h * (target_w / float(w)))))
|
|
177
|
+
return cv2.resize(img, (target_w, new_h), interpolation=cv2.INTER_AREA)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _downscale_if_needed(img, max_dim=20000):
|
|
181
|
+
h, w = img.shape[:2]
|
|
182
|
+
max_hw = max(h, w)
|
|
183
|
+
if max_hw <= max_dim:
|
|
184
|
+
return img
|
|
185
|
+
|
|
186
|
+
scale = float(max_dim) / float(max_hw)
|
|
187
|
+
new_w = max(1, int(round(w * scale)))
|
|
188
|
+
new_h = max(1, int(round(h * scale)))
|
|
189
|
+
print(f"\n\tDownscaling waterfall from {w}x{h} to {new_w}x{new_h} to control memory usage.")
|
|
190
|
+
return cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _fit_width(img, max_w):
|
|
194
|
+
h, w = img.shape[:2]
|
|
195
|
+
if w <= max_w:
|
|
196
|
+
return img
|
|
197
|
+
scale = float(max_w) / float(w)
|
|
198
|
+
new_h = max(1, int(round(h * scale)))
|
|
199
|
+
return cv2.resize(img, (max_w, new_h), interpolation=cv2.INTER_AREA)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _collect_mode_tile_paths(son, mode):
|
|
203
|
+
mode_dir = os.path.join(son.projDir, son.beamName, mode)
|
|
204
|
+
if not os.path.exists(mode_dir):
|
|
205
|
+
return []
|
|
206
|
+
|
|
207
|
+
exts = ['*.png', '*.jpg', '*.jpeg', '*.tif', '*.tiff']
|
|
208
|
+
paths = []
|
|
209
|
+
for ext in exts:
|
|
210
|
+
paths.extend(glob(os.path.join(mode_dir, ext)))
|
|
211
|
+
|
|
212
|
+
return _sort_tile_paths(paths)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _get_chunk_range_map(son):
|
|
216
|
+
chunk_range = {}
|
|
217
|
+
try:
|
|
218
|
+
meta_path = getattr(son, 'sonMetaFile', None)
|
|
219
|
+
if not meta_path or not os.path.exists(meta_path):
|
|
220
|
+
return chunk_range
|
|
221
|
+
|
|
222
|
+
df = pd.read_csv(meta_path, usecols=['chunk_id', 'ping_cnt', 'pixM'])
|
|
223
|
+
if len(df) == 0:
|
|
224
|
+
return chunk_range
|
|
225
|
+
|
|
226
|
+
df['chunk_id'] = pd.to_numeric(df['chunk_id'], errors='coerce')
|
|
227
|
+
df['ping_cnt'] = pd.to_numeric(df['ping_cnt'], errors='coerce')
|
|
228
|
+
df['pixM'] = pd.to_numeric(df['pixM'], errors='coerce')
|
|
229
|
+
df = df.dropna(subset=['chunk_id', 'ping_cnt', 'pixM'])
|
|
230
|
+
if len(df) == 0:
|
|
231
|
+
return chunk_range
|
|
232
|
+
|
|
233
|
+
# Approximate chunk range in meters from ping count and pixel size.
|
|
234
|
+
df['range_m'] = df['ping_cnt'] * df['pixM']
|
|
235
|
+
grouped = df.groupby('chunk_id', as_index=False)['range_m'].median()
|
|
236
|
+
for _, row in grouped.iterrows():
|
|
237
|
+
cid = int(row['chunk_id'])
|
|
238
|
+
rng = float(row['range_m'])
|
|
239
|
+
if np.isfinite(rng) and rng > 0:
|
|
240
|
+
chunk_range[cid] = rng
|
|
241
|
+
except Exception:
|
|
242
|
+
return {}
|
|
243
|
+
|
|
244
|
+
return chunk_range
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _range_reference_m(chunk_range_map):
|
|
248
|
+
if not chunk_range_map:
|
|
249
|
+
return None
|
|
250
|
+
vals = [float(v) for v in chunk_range_map.values() if np.isfinite(v) and float(v) > 0]
|
|
251
|
+
if len(vals) == 0:
|
|
252
|
+
return None
|
|
253
|
+
return float(np.median(vals))
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _pad_to_height(img, target_h):
|
|
257
|
+
h, w = img.shape[:2]
|
|
258
|
+
if h >= target_h:
|
|
259
|
+
return img
|
|
260
|
+
pad_h = target_h - h
|
|
261
|
+
return cv2.copyMakeBorder(img, 0, pad_h, 0, 0, cv2.BORDER_CONSTANT, value=0)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _pad_to_height_center(img, target_h):
|
|
265
|
+
h, w = img.shape[:2]
|
|
266
|
+
if h >= target_h:
|
|
267
|
+
return img
|
|
268
|
+
pad_h = target_h - h
|
|
269
|
+
top = pad_h // 2
|
|
270
|
+
bottom = pad_h - top
|
|
271
|
+
return cv2.copyMakeBorder(img, top, bottom, 0, 0, cv2.BORDER_CONSTANT, value=0)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _pad_to_width(img, target_w):
|
|
275
|
+
h, w = img.shape[:2]
|
|
276
|
+
if w >= target_w:
|
|
277
|
+
return img
|
|
278
|
+
pad_w = target_w - w
|
|
279
|
+
return cv2.copyMakeBorder(img, 0, 0, 0, pad_w, cv2.BORDER_CONSTANT, value=0)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _scale_tile_by_range(img, tile_path, chunk_range_map=None, ref_range_m=None, base_h=None):
|
|
283
|
+
if img is None:
|
|
284
|
+
return None
|
|
285
|
+
if not (chunk_range_map and ref_range_m and base_h):
|
|
286
|
+
return img
|
|
287
|
+
|
|
288
|
+
chunk_id = _extract_chunk_id_from_path(tile_path)
|
|
289
|
+
if chunk_id is None:
|
|
290
|
+
return img
|
|
291
|
+
|
|
292
|
+
rng = chunk_range_map.get(chunk_id, None)
|
|
293
|
+
if rng is None or (not np.isfinite(rng)) or rng <= 0:
|
|
294
|
+
return img
|
|
295
|
+
|
|
296
|
+
scale = float(rng) / float(ref_range_m)
|
|
297
|
+
scale = max(0.25, min(4.0, scale))
|
|
298
|
+
target_h = max(1, int(round(base_h * scale)))
|
|
299
|
+
return _resize_height(img, target_h)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def _build_strip_from_tiles(tile_paths, chunk_range_map=None, ref_range_m=None):
|
|
303
|
+
if len(tile_paths) == 0:
|
|
304
|
+
return None
|
|
305
|
+
|
|
306
|
+
imgs = []
|
|
307
|
+
base_h = None
|
|
308
|
+
if ref_range_m is None:
|
|
309
|
+
ref_range_m = _range_reference_m(chunk_range_map)
|
|
310
|
+
|
|
311
|
+
for path in tile_paths:
|
|
312
|
+
img = cv2.imread(path, cv2.IMREAD_UNCHANGED)
|
|
313
|
+
if img is None:
|
|
314
|
+
continue
|
|
315
|
+
|
|
316
|
+
if base_h is None:
|
|
317
|
+
base_h = img.shape[0]
|
|
318
|
+
|
|
319
|
+
if chunk_range_map and ref_range_m and base_h:
|
|
320
|
+
chunk_id = _extract_chunk_id_from_path(path)
|
|
321
|
+
rng = chunk_range_map.get(chunk_id, None)
|
|
322
|
+
if rng is not None and np.isfinite(rng) and rng > 0:
|
|
323
|
+
scale = float(rng) / float(ref_range_m)
|
|
324
|
+
scale = max(0.25, min(4.0, scale))
|
|
325
|
+
target_h = max(1, int(round(base_h * scale)))
|
|
326
|
+
img = _resize_height(img, target_h)
|
|
327
|
+
|
|
328
|
+
imgs.append(img)
|
|
329
|
+
|
|
330
|
+
if len(imgs) == 0:
|
|
331
|
+
return None
|
|
332
|
+
|
|
333
|
+
# Preserve range-driven vertical scaling by padding each tile to common max height.
|
|
334
|
+
max_h = max(img.shape[0] for img in imgs)
|
|
335
|
+
imgs = [_pad_to_height(img, max_h) for img in imgs]
|
|
336
|
+
strip = cv2.hconcat(imgs)
|
|
337
|
+
return strip
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _build_sidescan_combined(port_tiles, star_tiles, port_range_map=None, star_range_map=None, ref_range_m=None):
|
|
341
|
+
if len(port_tiles) == 0 or len(star_tiles) == 0:
|
|
342
|
+
return None
|
|
343
|
+
|
|
344
|
+
def _map_by_chunk(paths):
|
|
345
|
+
mapped = {}
|
|
346
|
+
for p in paths:
|
|
347
|
+
cid = _extract_chunk_id_from_path(p)
|
|
348
|
+
if cid is not None:
|
|
349
|
+
mapped[int(cid)] = p
|
|
350
|
+
return mapped
|
|
351
|
+
|
|
352
|
+
port_by_chunk = _map_by_chunk(port_tiles)
|
|
353
|
+
star_by_chunk = _map_by_chunk(star_tiles)
|
|
354
|
+
|
|
355
|
+
# Newest chunk first so newer data sits at the top of the stitched waterfall,
|
|
356
|
+
# while the oldest chunk remains at the bottom for the initial video frame.
|
|
357
|
+
common = sorted(set(port_by_chunk.keys()) & set(star_by_chunk.keys()), reverse=True)
|
|
358
|
+
if len(common) == 0:
|
|
359
|
+
pair_paths = list(zip(port_tiles, star_tiles))
|
|
360
|
+
pair_paths = list(reversed(pair_paths))
|
|
361
|
+
else:
|
|
362
|
+
pair_paths = [(port_by_chunk[c], star_by_chunk[c]) for c in common]
|
|
363
|
+
|
|
364
|
+
# Baseline heights for range-based scaling.
|
|
365
|
+
base_h_port = None
|
|
366
|
+
base_h_star = None
|
|
367
|
+
for p_path, s_path in pair_paths:
|
|
368
|
+
if base_h_port is None:
|
|
369
|
+
p_img = cv2.imread(p_path, cv2.IMREAD_UNCHANGED)
|
|
370
|
+
if p_img is not None:
|
|
371
|
+
base_h_port = p_img.shape[0]
|
|
372
|
+
if base_h_star is None:
|
|
373
|
+
s_img = cv2.imread(s_path, cv2.IMREAD_UNCHANGED)
|
|
374
|
+
if s_img is not None:
|
|
375
|
+
base_h_star = s_img.shape[0]
|
|
376
|
+
if (base_h_port is not None) and (base_h_star is not None):
|
|
377
|
+
break
|
|
378
|
+
|
|
379
|
+
rows = []
|
|
380
|
+
for p_path, s_path in pair_paths:
|
|
381
|
+
p_img = cv2.imread(p_path, cv2.IMREAD_UNCHANGED)
|
|
382
|
+
s_img = cv2.imread(s_path, cv2.IMREAD_UNCHANGED)
|
|
383
|
+
if p_img is None or s_img is None:
|
|
384
|
+
continue
|
|
385
|
+
|
|
386
|
+
p_img = _scale_tile_by_range(p_img, p_path, port_range_map, ref_range_m, base_h_port)
|
|
387
|
+
s_img = _scale_tile_by_range(s_img, s_path, star_range_map, ref_range_m, base_h_star)
|
|
388
|
+
|
|
389
|
+
# Apply side-scan waterfall geometry: rotate both CCW, then flip port to place nadir at center.
|
|
390
|
+
p_img = cv2.rotate(p_img, cv2.ROTATE_90_COUNTERCLOCKWISE)
|
|
391
|
+
s_img = cv2.rotate(s_img, cv2.ROTATE_90_COUNTERCLOCKWISE)
|
|
392
|
+
p_img = cv2.flip(p_img, 1)
|
|
393
|
+
|
|
394
|
+
p_img = _to_bgr8(p_img)
|
|
395
|
+
s_img = _to_bgr8(s_img)
|
|
396
|
+
|
|
397
|
+
row_h = max(p_img.shape[0], s_img.shape[0])
|
|
398
|
+
# Align each chunk pair on the vertical centerline.
|
|
399
|
+
p_img = _pad_to_height_center(p_img, row_h)
|
|
400
|
+
s_img = _pad_to_height_center(s_img, row_h)
|
|
401
|
+
|
|
402
|
+
# Keep chunk row widths comparable before vertical stacking.
|
|
403
|
+
row = cv2.hconcat([p_img, s_img])
|
|
404
|
+
rows.append(row)
|
|
405
|
+
|
|
406
|
+
if len(rows) == 0:
|
|
407
|
+
return None
|
|
408
|
+
|
|
409
|
+
max_w = max(r.shape[1] for r in rows)
|
|
410
|
+
rows = [_pad_to_width(r, max_w) for r in rows]
|
|
411
|
+
return cv2.vconcat(rows)
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def _window_positions(length, window_size, stride, reverse=False):
|
|
415
|
+
if length <= window_size:
|
|
416
|
+
positions = [0]
|
|
417
|
+
else:
|
|
418
|
+
positions = list(range(0, length - window_size + 1, stride))
|
|
419
|
+
if positions[-1] != (length - window_size):
|
|
420
|
+
positions.append(length - window_size)
|
|
421
|
+
|
|
422
|
+
if reverse:
|
|
423
|
+
positions = list(reversed(positions))
|
|
424
|
+
|
|
425
|
+
return positions
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def _export_scrolling_video(
|
|
429
|
+
img,
|
|
430
|
+
out_video,
|
|
431
|
+
axis='x',
|
|
432
|
+
reverse=False,
|
|
433
|
+
fps=10,
|
|
434
|
+
target_size=(1920, 1080),
|
|
435
|
+
stride=64,
|
|
436
|
+
):
|
|
437
|
+
os.makedirs(os.path.dirname(out_video), exist_ok=True)
|
|
438
|
+
|
|
439
|
+
h, w = img.shape[:2]
|
|
440
|
+
target_w, target_h = int(target_size[0]), int(target_size[1])
|
|
441
|
+
target_w = max(64, target_w)
|
|
442
|
+
target_h = max(64, target_h)
|
|
443
|
+
|
|
444
|
+
# Derive crop window from target resolution while preserving aspect ratio.
|
|
445
|
+
target_aspect = target_w / float(target_h)
|
|
446
|
+
win_w = min(w, target_w)
|
|
447
|
+
win_h = min(h, target_h)
|
|
448
|
+
if win_w <= 0 or win_h <= 0:
|
|
449
|
+
return
|
|
450
|
+
|
|
451
|
+
cur_aspect = win_w / float(win_h)
|
|
452
|
+
if cur_aspect > target_aspect:
|
|
453
|
+
win_w = max(1, int(round(win_h * target_aspect)))
|
|
454
|
+
else:
|
|
455
|
+
win_h = max(1, int(round(win_w / target_aspect)))
|
|
456
|
+
|
|
457
|
+
# If image is larger than target, prefer at least target-sized crop on scroll axis.
|
|
458
|
+
if axis == 'y' and h >= target_h:
|
|
459
|
+
win_h = target_h
|
|
460
|
+
win_w = min(w, max(1, int(round(win_h * target_aspect))))
|
|
461
|
+
elif axis != 'y' and w >= target_w:
|
|
462
|
+
win_w = target_w
|
|
463
|
+
win_h = min(h, max(1, int(round(win_w / target_aspect))))
|
|
464
|
+
|
|
465
|
+
# Ensure stride is valid and not too tiny for large windows.
|
|
466
|
+
stride = int(stride)
|
|
467
|
+
if stride <= 0:
|
|
468
|
+
stride = max(1, (win_h if axis == 'y' else win_w) // 10)
|
|
469
|
+
if axis == 'y':
|
|
470
|
+
win = min(win_h, h)
|
|
471
|
+
positions = _window_positions(h, win, stride, reverse=reverse)
|
|
472
|
+
first_frame = img[positions[0]:positions[0] + win, :win_w]
|
|
473
|
+
else:
|
|
474
|
+
win = min(win_w, w)
|
|
475
|
+
positions = _window_positions(w, win, stride, reverse=reverse)
|
|
476
|
+
first_frame = img[:win_h, positions[0]:positions[0] + win]
|
|
477
|
+
|
|
478
|
+
first_frame_bgr = _to_bgr8(first_frame)
|
|
479
|
+
frame_h, frame_w = target_h, target_w
|
|
480
|
+
writer = cv2.VideoWriter(
|
|
481
|
+
out_video,
|
|
482
|
+
cv2.VideoWriter_fourcc(*'mp4v'),
|
|
483
|
+
max(1, int(fps)),
|
|
484
|
+
(frame_w, frame_h),
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
if not writer.isOpened():
|
|
488
|
+
print(f"\n\tWARNING: Could not open video writer for {out_video}")
|
|
489
|
+
return
|
|
490
|
+
|
|
491
|
+
for pos in positions:
|
|
492
|
+
if axis == 'y':
|
|
493
|
+
frame = img[pos:pos + win, :win_w]
|
|
494
|
+
else:
|
|
495
|
+
frame = img[:win_h, pos:pos + win]
|
|
496
|
+
|
|
497
|
+
frame_bgr = _to_bgr8(frame)
|
|
498
|
+
if frame_bgr.shape[0] != frame_h or frame_bgr.shape[1] != frame_w:
|
|
499
|
+
frame_bgr = cv2.resize(frame_bgr, (frame_w, frame_h), interpolation=cv2.INTER_AREA)
|
|
500
|
+
writer.write(frame_bgr)
|
|
501
|
+
|
|
502
|
+
writer.release()
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def _export_waterfall_products(
|
|
506
|
+
sonObjs,
|
|
507
|
+
wcp,
|
|
508
|
+
wcm,
|
|
509
|
+
wcr,
|
|
510
|
+
wco,
|
|
511
|
+
mode_selection='auto',
|
|
512
|
+
ss_image=False,
|
|
513
|
+
ss_video=False,
|
|
514
|
+
di_image=False,
|
|
515
|
+
di_video=False,
|
|
516
|
+
fps=10,
|
|
517
|
+
video_resolution='1080p',
|
|
518
|
+
stride=64,
|
|
519
|
+
):
|
|
520
|
+
if not (ss_image or ss_video or di_image or di_video):
|
|
521
|
+
return
|
|
522
|
+
|
|
523
|
+
mode_flags = {
|
|
524
|
+
'wcp': bool(wcp),
|
|
525
|
+
'wcm': bool(wcm),
|
|
526
|
+
'src': bool(wcr),
|
|
527
|
+
'wco': bool(wco),
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
mode_selection = str(mode_selection).strip().lower()
|
|
531
|
+
if mode_selection in {'wcp', 'src', 'wcp+src'}:
|
|
532
|
+
if mode_selection == 'wcp':
|
|
533
|
+
modes = ['wcp']
|
|
534
|
+
elif mode_selection == 'src':
|
|
535
|
+
modes = ['src']
|
|
536
|
+
else:
|
|
537
|
+
modes = ['wcp', 'src']
|
|
538
|
+
else:
|
|
539
|
+
modes = [m for m, enabled in mode_flags.items() if enabled]
|
|
540
|
+
|
|
541
|
+
if len(modes) == 0:
|
|
542
|
+
print('\n\tWaterfall export requested, but no sonogram tile modes are enabled (wcp/wcm/wcr/wco). Skipping.')
|
|
543
|
+
return
|
|
544
|
+
|
|
545
|
+
res_key = str(video_resolution).strip().lower()
|
|
546
|
+
if res_key in {'4k', '2160p'}:
|
|
547
|
+
target_size = (3840, 2160)
|
|
548
|
+
elif res_key in {'1080p', 'fhd'}:
|
|
549
|
+
target_size = (1920, 1080)
|
|
550
|
+
elif res_key in {'720p', 'hd'}:
|
|
551
|
+
target_size = (1280, 720)
|
|
552
|
+
elif res_key in {'4xxp', '480p'}:
|
|
553
|
+
target_size = (854, 480)
|
|
554
|
+
else:
|
|
555
|
+
target_size = (1920, 1080)
|
|
556
|
+
|
|
557
|
+
print('\nGenerating waterfall products...')
|
|
558
|
+
|
|
559
|
+
# Group side-scan beams into port/star pairs by suffix key.
|
|
560
|
+
ss_groups = {}
|
|
561
|
+
down_beams = []
|
|
562
|
+
for son in sonObjs:
|
|
563
|
+
beam = str(getattr(son, 'beamName', ''))
|
|
564
|
+
if _is_sidescan_beam(beam):
|
|
565
|
+
key = _sidescan_group_key(beam)
|
|
566
|
+
group = ss_groups.setdefault(key, {})
|
|
567
|
+
if beam.startswith('ss_port'):
|
|
568
|
+
group['port'] = son
|
|
569
|
+
elif beam.startswith('ss_star'):
|
|
570
|
+
group['star'] = son
|
|
571
|
+
else:
|
|
572
|
+
down_beams.append(son)
|
|
573
|
+
|
|
574
|
+
# Side-scan: combine port/star into one waterfall and scroll top -> bottom.
|
|
575
|
+
if ss_image or ss_video:
|
|
576
|
+
ss_any = False
|
|
577
|
+
for group_key, group in sorted(ss_groups.items()):
|
|
578
|
+
if 'port' not in group or 'star' not in group:
|
|
579
|
+
continue
|
|
580
|
+
|
|
581
|
+
proj_dir = group['port'].projDir
|
|
582
|
+
port_range_map = _get_chunk_range_map(group['port'])
|
|
583
|
+
star_range_map = _get_chunk_range_map(group['star'])
|
|
584
|
+
shared_ref = _range_reference_m(port_range_map)
|
|
585
|
+
if shared_ref is None:
|
|
586
|
+
shared_ref = _range_reference_m(star_range_map)
|
|
587
|
+
|
|
588
|
+
for mode in modes:
|
|
589
|
+
port_tiles = _collect_mode_tile_paths(group['port'], mode)
|
|
590
|
+
star_tiles = _collect_mode_tile_paths(group['star'], mode)
|
|
591
|
+
if len(port_tiles) == 0 or len(star_tiles) == 0:
|
|
592
|
+
continue
|
|
593
|
+
|
|
594
|
+
ss_any = True
|
|
595
|
+
|
|
596
|
+
combined = _build_sidescan_combined(
|
|
597
|
+
port_tiles,
|
|
598
|
+
star_tiles,
|
|
599
|
+
port_range_map=port_range_map,
|
|
600
|
+
star_range_map=star_range_map,
|
|
601
|
+
ref_range_m=shared_ref,
|
|
602
|
+
)
|
|
603
|
+
if combined is None:
|
|
604
|
+
continue
|
|
605
|
+
|
|
606
|
+
# Ensure both port and star fully fit inside output frame width.
|
|
607
|
+
combined = _fit_width(combined, target_size[0])
|
|
608
|
+
combined = _downscale_if_needed(combined)
|
|
609
|
+
|
|
610
|
+
out_dir = os.path.join(proj_dir, 'waterfall_exports', 'sidescan', mode)
|
|
611
|
+
os.makedirs(out_dir, exist_ok=True)
|
|
612
|
+
|
|
613
|
+
if ss_image:
|
|
614
|
+
cv2.imwrite(os.path.join(out_dir, 'waterfall.png'), combined)
|
|
615
|
+
|
|
616
|
+
if ss_video:
|
|
617
|
+
_export_scrolling_video(
|
|
618
|
+
combined,
|
|
619
|
+
os.path.join(out_dir, 'waterfall_scroll_t2b.mp4'),
|
|
620
|
+
axis='y',
|
|
621
|
+
reverse=True,
|
|
622
|
+
fps=fps,
|
|
623
|
+
target_size=target_size,
|
|
624
|
+
stride=stride,
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
if not ss_any:
|
|
628
|
+
print('\n\tNo side-scan tiles found for requested waterfall mode(s).')
|
|
629
|
+
print('\tEnable or select matching mode(s), e.g., WCP and/or SRC.')
|
|
630
|
+
|
|
631
|
+
# Down-imaging: generate per-beam waterfall and scroll right -> left.
|
|
632
|
+
if di_image or di_video:
|
|
633
|
+
di_any = False
|
|
634
|
+
for son in down_beams:
|
|
635
|
+
beam = str(getattr(son, 'beamName', 'unknown_beam'))
|
|
636
|
+
proj_dir = son.projDir
|
|
637
|
+
down_range_map = _get_chunk_range_map(son)
|
|
638
|
+
down_ref = _range_reference_m(down_range_map)
|
|
639
|
+
|
|
640
|
+
for mode in modes:
|
|
641
|
+
tiles = _collect_mode_tile_paths(son, mode)
|
|
642
|
+
if len(tiles) == 0:
|
|
643
|
+
continue
|
|
644
|
+
|
|
645
|
+
di_any = True
|
|
646
|
+
|
|
647
|
+
strip = _build_strip_from_tiles(
|
|
648
|
+
tiles,
|
|
649
|
+
chunk_range_map=down_range_map,
|
|
650
|
+
ref_range_m=down_ref,
|
|
651
|
+
)
|
|
652
|
+
if strip is None:
|
|
653
|
+
continue
|
|
654
|
+
|
|
655
|
+
strip = _downscale_if_needed(strip)
|
|
656
|
+
|
|
657
|
+
out_dir = os.path.join(proj_dir, 'waterfall_exports', 'down_imaging', mode)
|
|
658
|
+
os.makedirs(out_dir, exist_ok=True)
|
|
659
|
+
|
|
660
|
+
if di_image:
|
|
661
|
+
cv2.imwrite(os.path.join(out_dir, f'{beam}_waterfall.png'), strip)
|
|
662
|
+
|
|
663
|
+
if di_video:
|
|
664
|
+
_export_scrolling_video(
|
|
665
|
+
strip,
|
|
666
|
+
os.path.join(out_dir, f'{beam}_waterfall_scroll.mp4'),
|
|
667
|
+
axis='x',
|
|
668
|
+
reverse=False,
|
|
669
|
+
fps=fps,
|
|
670
|
+
target_size=target_size,
|
|
671
|
+
stride=stride,
|
|
672
|
+
)
|
|
673
|
+
|
|
674
|
+
if not di_any:
|
|
675
|
+
print('\n\tNo down-imaging tiles found for requested waterfall mode(s).')
|
|
676
|
+
print('\tEnable or select matching mode(s), e.g., WCP and/or SRC.')
|
|
677
|
+
|
|
678
|
+
|
|
108
679
|
#===========================================
|
|
109
680
|
def read_master_func(logfilename='',
|
|
110
681
|
project_mode=0,
|
|
@@ -179,6 +750,14 @@ def read_master_func(logfilename='',
|
|
|
179
750
|
map_mosaic=0,
|
|
180
751
|
banklines=False,
|
|
181
752
|
side_scan_only=False,
|
|
753
|
+
waterfall_ss_image=False,
|
|
754
|
+
waterfall_ss_video=False,
|
|
755
|
+
waterfall_di_image=False,
|
|
756
|
+
waterfall_di_video=False,
|
|
757
|
+
waterfall_video_fps=10,
|
|
758
|
+
waterfall_video_resolution='1080p',
|
|
759
|
+
waterfall_mode_selection='auto',
|
|
760
|
+
waterfall_window_stride=64,
|
|
182
761
|
return_context=False,
|
|
183
762
|
**kwargs):
|
|
184
763
|
|
|
@@ -590,6 +1169,17 @@ def read_master_func(logfilename='',
|
|
|
590
1169
|
# Decode DAT file (varies by model) #
|
|
591
1170
|
############################################################################
|
|
592
1171
|
|
|
1172
|
+
wf_mode = str(waterfall_mode_selection).strip().lower()
|
|
1173
|
+
wf_requested = bool(waterfall_ss_image) or bool(waterfall_ss_video) or bool(waterfall_di_image) or bool(waterfall_di_video)
|
|
1174
|
+
if wf_requested:
|
|
1175
|
+
if wf_mode == 'wcp':
|
|
1176
|
+
wcp = True
|
|
1177
|
+
elif wf_mode == 'src':
|
|
1178
|
+
wcr = True
|
|
1179
|
+
elif wf_mode == 'wcp+src':
|
|
1180
|
+
wcp = True
|
|
1181
|
+
wcr = True
|
|
1182
|
+
|
|
593
1183
|
if (project_mode != 2):
|
|
594
1184
|
|
|
595
1185
|
#####
|
|
@@ -1708,6 +2298,32 @@ def read_master_func(logfilename='',
|
|
|
1708
2298
|
print("Time (s):", round(time.time() - start_time, ndigits=1))
|
|
1709
2299
|
printUsage()
|
|
1710
2300
|
|
|
2301
|
+
if bool(waterfall_ss_image) or bool(waterfall_ss_video) or bool(waterfall_di_image) or bool(waterfall_di_video):
|
|
2302
|
+
start_time = time.time()
|
|
2303
|
+
print("\nGenerating waterfall image/video exports...")
|
|
2304
|
+
try:
|
|
2305
|
+
_export_waterfall_products(
|
|
2306
|
+
sonObjs,
|
|
2307
|
+
wcp=wcp,
|
|
2308
|
+
wcm=wcm,
|
|
2309
|
+
wcr=wcr,
|
|
2310
|
+
wco=wco,
|
|
2311
|
+
mode_selection=waterfall_mode_selection,
|
|
2312
|
+
ss_image=bool(waterfall_ss_image),
|
|
2313
|
+
ss_video=bool(waterfall_ss_video),
|
|
2314
|
+
di_image=bool(waterfall_di_image),
|
|
2315
|
+
di_video=bool(waterfall_di_video),
|
|
2316
|
+
fps=int(waterfall_video_fps),
|
|
2317
|
+
video_resolution=str(waterfall_video_resolution),
|
|
2318
|
+
stride=int(waterfall_window_stride),
|
|
2319
|
+
)
|
|
2320
|
+
except Exception as e:
|
|
2321
|
+
print(f"\nWaterfall export failed: {e}")
|
|
2322
|
+
|
|
2323
|
+
print("Done!")
|
|
2324
|
+
print("Time (s):", round(time.time() - start_time, ndigits=1))
|
|
2325
|
+
printUsage()
|
|
2326
|
+
|
|
1711
2327
|
##############################################
|
|
1712
2328
|
# Let's pickle sonObj so we can reload later #
|
|
1713
2329
|
##############################################
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '5.5.0'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pingmapper
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.5.0
|
|
4
4
|
Summary: Open-source interface for processing recreation-grade side scan sonar datasets and reproducibly mapping benthic habitat
|
|
5
5
|
Author: Cameron Bodine, Daniel Buscombe, Iury T Simoes-Sousa
|
|
6
6
|
Author-email: bodine.cs@gmail.email
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '5.4.9'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/DRAFT_Workflows/avg_predictions_Mussel_WBL.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/DRAFT_Workflows/testEXAMPLE_mosaic_logit.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pingmapper-5.4.9 → pingmapper-5.5.0}/pingmapper/utils/Substrate_Summaries/03_gen_summary_shp.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|