pingmapper 5.3.0__tar.gz → 5.3.2__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.3.0 → pingmapper-5.3.2}/PKG-INFO +1 -1
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/class_mapSubstrateObj.py +14 -2
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/class_portstarObj.py +96 -70
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/class_sonObj.py +40 -28
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/class_sonObj_nadirgaptest.py +37 -48
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/doWork.py +21 -1
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/funcs_common.py +6 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/funcs_model.py +64 -3
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/main_mapSubstrate.py +16 -1
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/main_readFiles.py +66 -46
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/main_rectify.py +9 -0
- pingmapper-5.3.2/pingmapper/version.py +1 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper.egg-info/PKG-INFO +1 -1
- pingmapper-5.3.0/pingmapper/version.py +0 -1
- {pingmapper-5.3.0 → pingmapper-5.3.2}/LICENSE +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/README.md +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/__init__.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/__main__.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/class_rectObj.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/default_params.json +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/funcs_rectify.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/gui_main.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/processing_scripts/main_batchDirectory_2024-01-18_0926.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/processing_scripts/main_batchDirectory_2024-01-18_0929.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/scratch/funcs_pyhum_correct.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/scratch/main.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/scratch/main_batchDirectory.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/test_PINGMapper.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/test_time.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/DRAFT_Workflows/avg_predictions_Mussel_WBL.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/DRAFT_Workflows/gen_centerline.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/DRAFT_Workflows/gen_centerline_from_bankline.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/DRAFT_Workflows/gen_centerline_trkpnts_fitspline_DRAFT.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/DRAFT_Workflows/testEXAMPLE_mosaic_logit.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/RawEGN_avg_predictions.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/Substrate_Summaries/00_substrate_logits_mosaic_transects.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/Substrate_Summaries/00_substrate_shps_mosaic_transects.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/Substrate_Summaries/01_gen_centerline_from_coverage.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/Substrate_Summaries/02_gen_summary_stamp_shps.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/Substrate_Summaries/03_gen_summary_shp.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/Substrate_Summaries/04_combine_summary_shp_csv.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/Substrate_Summaries/05_gen_summary_shp_plots.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/Substrate_Summaries/06_compare_raw-egn_volume.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/Substrate_Summaries/08_raw-egn_hardReacheFreq_hist.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/Substrate_Summaries/09_raw-egn_PatchSize_density.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/Substrate_Summaries/summarize_project_substrate.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/export_coverage.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper/utils/main_mosaic_transects.py +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper.egg-info/SOURCES.txt +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper.egg-info/dependency_links.txt +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper.egg-info/requires.txt +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pingmapper.egg-info/top_level.txt +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/pyproject.toml +0 -0
- {pingmapper-5.3.0 → pingmapper-5.3.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pingmapper
|
|
3
|
-
Version: 5.3.
|
|
3
|
+
Version: 5.3.2
|
|
4
4
|
Summary: Open-source interface for processing recreation-grade side scan sonar datasets and reproducibly mapping benthic habitat
|
|
5
5
|
Author: Daniel Buscombe
|
|
6
6
|
Author-email: Cameron Bodine <bodine.cs@gmail.email>
|
|
@@ -119,7 +119,7 @@ class mapSubObj(rectObj):
|
|
|
119
119
|
# Save predictions to npz
|
|
120
120
|
self._saveSubstrateNpz(substratePred, i, MY_CLASS_NAMES)
|
|
121
121
|
|
|
122
|
-
del
|
|
122
|
+
del substratePred
|
|
123
123
|
gc.collect()
|
|
124
124
|
|
|
125
125
|
return
|
|
@@ -198,8 +198,20 @@ class mapSubObj(rectObj):
|
|
|
198
198
|
del sonWin, wStart, wEnd, softmax_score, est_label
|
|
199
199
|
|
|
200
200
|
# Take mean across all windows to get one final softmax_score array
|
|
201
|
-
|
|
201
|
+
# Compute nan-mean incrementally to avoid allocating a large stacked array
|
|
202
|
+
_first = winSoftMax[0]
|
|
203
|
+
fSum = np.where(~np.isnan(_first), _first, 0.0)
|
|
204
|
+
fCount = (~np.isnan(_first)).astype(np.float32)
|
|
205
|
+
del _first
|
|
206
|
+
for _arr in winSoftMax[1:]:
|
|
207
|
+
_valid = ~np.isnan(_arr)
|
|
208
|
+
fSum += np.where(_valid, _arr, 0.0)
|
|
209
|
+
fCount += _valid
|
|
210
|
+
del _arr, _valid
|
|
202
211
|
del winSoftMax
|
|
212
|
+
with np.errstate(invalid='ignore'):
|
|
213
|
+
fSoftmax = np.where(fCount > 0, fSum / fCount, np.nan)
|
|
214
|
+
del fSum, fCount
|
|
203
215
|
|
|
204
216
|
# Crop center chunk predictions and recover original dims
|
|
205
217
|
h, w = origDims # Center chunks original dims
|
|
@@ -1349,7 +1349,7 @@ class portstarObj(object):
|
|
|
1349
1349
|
del son3bnd, init_label, init_prob, crop_label, crop_prob, sonCrop
|
|
1350
1350
|
del maxDepths, minDepths, avgDepths, Wp, portDepPixCrop, starDepPixCrop
|
|
1351
1351
|
del portDepPix, starDepPix
|
|
1352
|
-
del model
|
|
1352
|
+
del model
|
|
1353
1353
|
|
|
1354
1354
|
# return #self
|
|
1355
1355
|
return portDepPixFinal, starDepPixFinal, i
|
|
@@ -2052,7 +2052,7 @@ class portstarObj(object):
|
|
|
2052
2052
|
port_pix = self._getShadowPix(port_label, remShadow)
|
|
2053
2053
|
star_pix = self._getShadowPix(star_label, remShadow)
|
|
2054
2054
|
|
|
2055
|
-
del
|
|
2055
|
+
del model
|
|
2056
2056
|
gc.collect()
|
|
2057
2057
|
return i, port_pix, star_pix
|
|
2058
2058
|
|
|
@@ -2101,48 +2101,24 @@ class portstarObj(object):
|
|
|
2101
2101
|
del son.sonDat
|
|
2102
2102
|
|
|
2103
2103
|
# Get rid of zeros along water column area
|
|
2104
|
+
# Vectorized: replace class-8 (water column) labels in non-WC area with 0
|
|
2105
|
+
dep_arr = np.asarray(son.bedPick, dtype=int) # (W,)
|
|
2106
|
+
row_idx = np.arange(label.shape[0])[:, np.newaxis] # (H, 1)
|
|
2107
|
+
below_wc = row_idx >= dep_arr[np.newaxis, :] # (H, W)
|
|
2108
|
+
label = np.where(below_wc & (label == 8), 0, label)
|
|
2109
|
+
|
|
2110
|
+
# Zero-fill per ping: propagate the class at the edge of any zero region
|
|
2111
|
+
# into that region. Zeros should form at most one contiguous block per ping
|
|
2112
|
+
# (the comment in the original confirms this). Skip pings with no zeros.
|
|
2104
2113
|
for p in range(label.shape[1]):
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
# Remove water column
|
|
2115
|
-
ping = ping[d:]
|
|
2116
|
-
|
|
2117
|
-
# If any water column pics remain, set to zero
|
|
2118
|
-
ping = np.where(ping==8, 0, ping)
|
|
2119
|
-
|
|
2120
|
-
##############
|
|
2121
|
-
# Remove zeros
|
|
2122
|
-
# Find zeros. Should be grouped in contiguous arrays (array[0, 1, 2], array[100, 101, 102], ...
|
|
2123
|
-
zero = np.where(ping==0)
|
|
2124
|
-
|
|
2125
|
-
if len(zero[0])>0:
|
|
2126
|
-
for z in zero:
|
|
2127
|
-
# Get index of first and last zero
|
|
2128
|
-
f, l = z[0], z[-1]
|
|
2129
|
-
|
|
2130
|
-
# Don't fall off edge
|
|
2131
|
-
if l+1 < ping.shape[0]:
|
|
2132
|
-
|
|
2133
|
-
# Get classification of next pixel
|
|
2134
|
-
c = ping[l+1]
|
|
2135
|
-
|
|
2136
|
-
# Fill zero region with c
|
|
2137
|
-
ping[f:l+1] = c
|
|
2138
|
-
|
|
2139
|
-
# Add water column back in
|
|
2140
|
-
ping = list(wc)+list(ping)
|
|
2141
|
-
|
|
2142
|
-
# Update objects filled with ping
|
|
2143
|
-
label[:, p] = ping
|
|
2144
|
-
|
|
2145
|
-
del ping
|
|
2114
|
+
d = int(son.bedPick[p])
|
|
2115
|
+
ping_below = label[d:, p]
|
|
2116
|
+
zero_idx = np.where(ping_below == 0)[0]
|
|
2117
|
+
if len(zero_idx) == 0:
|
|
2118
|
+
continue
|
|
2119
|
+
f, l = int(zero_idx[0]), int(zero_idx[-1])
|
|
2120
|
+
if d + l + 1 < label.shape[0]:
|
|
2121
|
+
label[d + f : d + l + 1, p] = ping_below[l + 1]
|
|
2146
2122
|
|
|
2147
2123
|
son.sonDat = label
|
|
2148
2124
|
|
|
@@ -2299,30 +2275,58 @@ class portstarObj(object):
|
|
|
2299
2275
|
# # Do shadow and water column removal
|
|
2300
2276
|
|
|
2301
2277
|
# Store pred stack in variable
|
|
2302
|
-
predStack = son.sonDat
|
|
2303
|
-
|
|
2304
|
-
# Iterate each classification layer
|
|
2305
|
-
for c in range(classes):
|
|
2306
|
-
# Get class prediction
|
|
2307
|
-
son.sonDat = predStack[:,:,c]
|
|
2308
|
-
|
|
2309
|
-
# Remove shadows
|
|
2310
|
-
# Get mask
|
|
2311
|
-
son._SHW_mask(chunk, son=False)
|
|
2278
|
+
predStack = son.sonDat # (H, W, C)
|
|
2279
|
+
H_pred, W_pred, C_pred = predStack.shape
|
|
2312
2280
|
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
# Update predStack
|
|
2320
|
-
predStack[:,:,c] = son.sonDat
|
|
2321
|
-
|
|
2322
|
-
del son.sonDat
|
|
2281
|
+
# Compute shadow mask ONCE — geometry depends only on son.shadow[chunk],
|
|
2282
|
+
# not on which class band we are processing.
|
|
2283
|
+
son.sonDat = predStack[:, :, 0]
|
|
2284
|
+
son._SHW_mask(chunk, son=False)
|
|
2285
|
+
shw_mask = son.shadowMask # (H, W)
|
|
2286
|
+
del son.shadowMask
|
|
2323
2287
|
|
|
2324
|
-
#
|
|
2325
|
-
|
|
2288
|
+
# Apply shadow mask to all classes in a single vectorised broadcast
|
|
2289
|
+
predStack = predStack * shw_mask[:, :, np.newaxis]
|
|
2290
|
+
|
|
2291
|
+
# Apply slant range correction (SRC) to all classes in one pass.
|
|
2292
|
+
# Original _WCR_SRC ran an O(H) Python inner loop for every (class, ping)
|
|
2293
|
+
# pair. Here we vectorise the H dimension with numpy and process all C
|
|
2294
|
+
# classes simultaneously, reducing Python iterations from C×W×H → W×C.
|
|
2295
|
+
bedPick = round(sonMeta['dep_m'] / sonMeta['pixM'], 0).astype(int).reset_index(drop=True)
|
|
2296
|
+
srcStack = np.zeros((H_pred, W_pred, C_pred), dtype=np.float32)
|
|
2297
|
+
for j in range(W_pred):
|
|
2298
|
+
depth = int(bedPick[j])
|
|
2299
|
+
if depth >= H_pred:
|
|
2300
|
+
continue
|
|
2301
|
+
dd = float(depth * depth)
|
|
2302
|
+
row_arr = np.arange(depth, H_pred)
|
|
2303
|
+
src_idx = np.round(
|
|
2304
|
+
np.sqrt(row_arr.astype(np.float64) ** 2 - dd)
|
|
2305
|
+
).astype(int)
|
|
2306
|
+
valid = src_idx < H_pred
|
|
2307
|
+
rows_v = row_arr[valid]
|
|
2308
|
+
sidx_v = src_idx[valid]
|
|
2309
|
+
if len(sidx_v) == 0:
|
|
2310
|
+
continue
|
|
2311
|
+
data_extent = int(sidx_v[-1])
|
|
2312
|
+
|
|
2313
|
+
# Scatter all C classes at once, then zero past range extent
|
|
2314
|
+
pingStack = np.full((H_pred, C_pred), np.nan, dtype=np.float32)
|
|
2315
|
+
pingStack[sidx_v, :] = predStack[rows_v, j, :].astype(np.float32)
|
|
2316
|
+
pingStack[data_extent:, :] = 0
|
|
2317
|
+
|
|
2318
|
+
# Interpolate gaps — NaN pattern is geometry-driven, same for all classes
|
|
2319
|
+
nans = np.isnan(pingStack[:, 0])
|
|
2320
|
+
if nans.any():
|
|
2321
|
+
x = np.arange(H_pred)
|
|
2322
|
+
xnans, xvalid = x[nans], x[~nans]
|
|
2323
|
+
for c in range(C_pred):
|
|
2324
|
+
pingStack[nans, c] = np.interp(
|
|
2325
|
+
xnans, xvalid, pingStack[~nans, c]
|
|
2326
|
+
)
|
|
2327
|
+
srcStack[:, j, :] = pingStack
|
|
2328
|
+
|
|
2329
|
+
son.sonDat = srcStack
|
|
2326
2330
|
|
|
2327
2331
|
# Iterate each class again and:
|
|
2328
2332
|
for c in range(classes):
|
|
@@ -2411,6 +2415,20 @@ class portstarObj(object):
|
|
|
2411
2415
|
|
|
2412
2416
|
|
|
2413
2417
|
|
|
2418
|
+
#=======================================================================
|
|
2419
|
+
def _preloadRectifyCache(self):
|
|
2420
|
+
'''
|
|
2421
|
+
Pre-load the trackline and sonar-metadata CSVs that _rectify reads
|
|
2422
|
+
on every chunk call. Call this ONCE on the portstarObj in the main
|
|
2423
|
+
process before Parallel dispatch so that each serialised copy of the
|
|
2424
|
+
object already has the data, avoiding per-chunk disk reads.
|
|
2425
|
+
'''
|
|
2426
|
+
portTrkMetaFile = os.path.join(self.port.metaDir, "Trackline_Smth_"+self.port.beamName+".csv")
|
|
2427
|
+
starTrkMetaFile = os.path.join(self.star.metaDir, "Trackline_Smth_"+self.star.beamName+".csv")
|
|
2428
|
+
self.port._trkMetaDF = pd.read_csv(portTrkMetaFile)
|
|
2429
|
+
self.star._trkMetaDF = pd.read_csv(starTrkMetaFile)
|
|
2430
|
+
self.port._loadSonMeta() # populates self.port.sonMetaDF
|
|
2431
|
+
|
|
2414
2432
|
#=======================================================================
|
|
2415
2433
|
def _rectify(self, dat, chunk, imgOutPrefix, filt=50, wgs=False, return_rect=False):
|
|
2416
2434
|
'''
|
|
@@ -2473,8 +2491,11 @@ class portstarObj(object):
|
|
|
2473
2491
|
|
|
2474
2492
|
###
|
|
2475
2493
|
# Get top (port range) coordinates
|
|
2476
|
-
|
|
2477
|
-
|
|
2494
|
+
# Cache the full CSV on self.port so we only read from disk once per portstarObj
|
|
2495
|
+
if not hasattr(self.port, '_trkMetaDF') or self.port._trkMetaDF is None:
|
|
2496
|
+
self.port._trkMetaDF = pd.read_csv(portTrkMetaFile)
|
|
2497
|
+
portTrkFull = self.port._trkMetaDF
|
|
2498
|
+
trkMeta = portTrkFull[portTrkFull['chunk_id']==chunk].reset_index(drop=False)
|
|
2478
2499
|
|
|
2479
2500
|
# Get range (outer extent) coordinates [xR, yR] to transposed numpy arrays
|
|
2480
2501
|
xTop, yTop = trkMeta[xRange].to_numpy().T, trkMeta[yRange].to_numpy().T
|
|
@@ -2482,8 +2503,11 @@ class portstarObj(object):
|
|
|
2482
2503
|
|
|
2483
2504
|
###
|
|
2484
2505
|
# Get bottom (star range) coordinates
|
|
2485
|
-
|
|
2486
|
-
|
|
2506
|
+
# Cache the full CSV on self.star so we only read from disk once per portstarObj
|
|
2507
|
+
if not hasattr(self.star, '_trkMetaDF') or self.star._trkMetaDF is None:
|
|
2508
|
+
self.star._trkMetaDF = pd.read_csv(starTrkMetaFile)
|
|
2509
|
+
starTrkFull = self.star._trkMetaDF
|
|
2510
|
+
trkMeta = starTrkFull[starTrkFull['chunk_id']==chunk].reset_index(drop=False)
|
|
2487
2511
|
|
|
2488
2512
|
# Get range (outer extent) coordinates [xR, yR] to transposed numpy arrays
|
|
2489
2513
|
xBot, yBot = trkMeta[xRange].to_numpy().T, trkMeta[yRange].to_numpy().T
|
|
@@ -2507,7 +2531,9 @@ class portstarObj(object):
|
|
|
2507
2531
|
|
|
2508
2532
|
# Get pixel size
|
|
2509
2533
|
# pix_m = self.port.pixM
|
|
2510
|
-
|
|
2534
|
+
# Cache sonMetaDF — _loadSonMeta reads the same CSV every call
|
|
2535
|
+
if not hasattr(self.port, 'sonMetaDF') or self.port.sonMetaDF is None:
|
|
2536
|
+
self.port._loadSonMeta()
|
|
2511
2537
|
isChunk = self.port.sonMetaDF['chunk_id']==chunk
|
|
2512
2538
|
sonMeta = self.port.sonMetaDF[isChunk].reset_index()
|
|
2513
2539
|
|
|
@@ -1310,43 +1310,55 @@ class sonObj(object):
|
|
|
1310
1310
|
# bedPick = round(sonMeta['dep_m'] / sonMeta['pix_m'], 0).astype(int)
|
|
1311
1311
|
bedPick = round(sonMeta['dep_m'] / sonMeta['pixM'], 0).astype(int).reset_index(drop=True)
|
|
1312
1312
|
|
|
1313
|
+
H, W = self.sonDat.shape[0], self.sonDat.shape[1]
|
|
1314
|
+
|
|
1313
1315
|
# Initialize 2d array to store relocated sonar records
|
|
1314
|
-
srcDat = np.zeros((
|
|
1316
|
+
srcDat = np.zeros((H, W), dtype=np.float32)
|
|
1317
|
+
|
|
1318
|
+
# Iterate each ping. The inner row-loop is replaced with numpy vector
|
|
1319
|
+
# ops: compute all slant→horizontal index mappings at once, scatter
|
|
1320
|
+
# intensities, then interpolate gaps.
|
|
1321
|
+
for j in range(W):
|
|
1322
|
+
depth = int(bedPick[j]) # depth at nadir (pixels)
|
|
1323
|
+
if depth >= H:
|
|
1324
|
+
continue
|
|
1325
|
+
dd = float(depth * depth)
|
|
1326
|
+
|
|
1327
|
+
# Rows at or beyond the water column boundary
|
|
1328
|
+
row_arr = np.arange(depth, H)
|
|
1329
|
+
# Horizontal range index via Pythagorean theorem (vectorised)
|
|
1330
|
+
src_idx = np.round(
|
|
1331
|
+
np.sqrt(row_arr.astype(np.float64) ** 2 - dd)
|
|
1332
|
+
).astype(int)
|
|
1333
|
+
|
|
1334
|
+
# Discard out-of-bounds indices
|
|
1335
|
+
valid = src_idx < H
|
|
1336
|
+
rows_v = row_arr[valid]
|
|
1337
|
+
sidx_v = src_idx[valid]
|
|
1338
|
+
if len(sidx_v) == 0:
|
|
1339
|
+
continue
|
|
1315
1340
|
|
|
1316
|
-
|
|
1317
|
-
for j in range(self.sonDat.shape[1]):
|
|
1318
|
-
depth = bedPick[j] # Get depth (in pixels) at nadir
|
|
1319
|
-
dd = depth**2
|
|
1320
|
-
# Create 1d array to store relocated bed pixels. Set to nan so we
|
|
1321
|
-
## can later interpolate over gaps.
|
|
1322
|
-
pingDat = (np.ones((self.sonDat.shape[0])).astype(np.float32)) * np.nan
|
|
1323
|
-
dataExtent = 0
|
|
1324
|
-
#Iterate each sonar/ping return
|
|
1325
|
-
for i in range(self.sonDat.shape[0]):
|
|
1326
|
-
if i >= depth:
|
|
1327
|
-
intensity = self.sonDat[i,j] # Get the intensity value
|
|
1328
|
-
srcIndex = int(round(math.sqrt(i**2 - dd),0)) #Calculate horizontal range (in pixels) using pathagorean theorem
|
|
1329
|
-
pingDat[srcIndex] = intensity # Store intensity at appropriate horizontal range
|
|
1330
|
-
dataExtent = srcIndex # Store range extent (max range) of ping
|
|
1331
|
-
else:
|
|
1332
|
-
pass
|
|
1333
|
-
pingDat[dataExtent:]=0 # Zero out values past range extent so we don't interpolate past this
|
|
1341
|
+
data_extent = int(sidx_v[-1])
|
|
1334
1342
|
|
|
1335
|
-
#
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
pingDat[
|
|
1343
|
+
# Scatter intensities; duplicate src_idx writes keep last value
|
|
1344
|
+
pingDat = np.full(H, np.nan, dtype=np.float32)
|
|
1345
|
+
pingDat[sidx_v] = self.sonDat[rows_v, j].astype(np.float32)
|
|
1346
|
+
pingDat[data_extent:] = 0 # zero past range extent
|
|
1347
|
+
|
|
1348
|
+
# Interpolate across-track gaps introduced by SRC
|
|
1349
|
+
nans = np.isnan(pingDat)
|
|
1350
|
+
if nans.any():
|
|
1351
|
+
x = np.arange(H)
|
|
1352
|
+
pingDat[nans] = np.interp(x[nans], x[~nans], pingDat[~nans])
|
|
1339
1353
|
|
|
1340
1354
|
# Store relocated ping in output array
|
|
1341
1355
|
if son:
|
|
1342
|
-
srcDat[:,j] = np.around(pingDat, 0)
|
|
1356
|
+
srcDat[:, j] = np.around(pingDat, 0)
|
|
1343
1357
|
else:
|
|
1344
|
-
srcDat[:,j] = pingDat
|
|
1345
|
-
|
|
1346
|
-
del pingDat
|
|
1358
|
+
srcDat[:, j] = pingDat
|
|
1347
1359
|
|
|
1348
1360
|
if son:
|
|
1349
|
-
self.sonDat = srcDat.astype(int)
|
|
1361
|
+
self.sonDat = srcDat.astype(int) # Store in class attribute for later use
|
|
1350
1362
|
else:
|
|
1351
1363
|
self.sonDat = srcDat
|
|
1352
1364
|
del srcDat
|
|
@@ -934,75 +934,64 @@ class sonObj(object):
|
|
|
934
934
|
# bedPick = round(sonMeta['dep_m'] / sonMeta['pix_m'], 0).astype(int)
|
|
935
935
|
bedPick = round(sonMeta['dep_m'] / sonMeta['pixM'], 0).astype(int).reset_index(drop=True)
|
|
936
936
|
|
|
937
|
+
H, W = self.sonDat.shape[0], self.sonDat.shape[1]
|
|
938
|
+
|
|
937
939
|
# Get max depth
|
|
938
940
|
maxDep = max(bedPick)
|
|
939
941
|
maxGap = int(round(np.tan(np.deg2rad(4)) * maxDep, 0)) # Max nadir gap (assume 4 degrees)
|
|
940
942
|
|
|
941
|
-
|
|
942
|
-
srcDat = np.zeros((self.sonDat.shape[0]+maxGap, self.sonDat.shape[1])).astype(np.float32)#.astype(int)
|
|
943
|
+
H_out = H + maxGap
|
|
943
944
|
|
|
944
|
-
|
|
945
|
+
# Initialize 2d array to store relocated sonar records
|
|
946
|
+
srcDat = np.zeros((H_out, W), dtype=np.float32)
|
|
945
947
|
|
|
946
|
-
#Iterate each ping
|
|
947
|
-
for j in range(
|
|
948
|
-
depth = bedPick[j]
|
|
949
|
-
dd = depth
|
|
948
|
+
#Iterate each ping (inner row-loop replaced with numpy vector ops)
|
|
949
|
+
for j in range(W):
|
|
950
|
+
depth = int(bedPick[j]) # Get depth (in pixels) at nadir
|
|
951
|
+
dd = float(depth * depth)
|
|
950
952
|
|
|
951
|
-
# Calculate gap
|
|
953
|
+
# Calculate nadir gap (assume 4 degrees) for this ping
|
|
952
954
|
nadirGap = int(round(np.tan(np.deg2rad(4)) * depth, 0))
|
|
953
955
|
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
#
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
# # Calculate nadir gap (assume 4 degrees)
|
|
962
|
-
# nadirGap = int(round(np.tan(np.deg2rad(4)) * depth, 0))
|
|
956
|
+
# Rows at or beyond the water column boundary
|
|
957
|
+
row_arr = np.arange(depth, H)
|
|
958
|
+
# Horizontal range via Pythagorean theorem, plus nadir gap offset
|
|
959
|
+
src_idx = np.round(
|
|
960
|
+
np.sqrt(row_arr.astype(np.float64) ** 2 - dd)
|
|
961
|
+
).astype(int) + nadirGap
|
|
963
962
|
|
|
964
|
-
#
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
srcIndex = int(round(math.sqrt((i)**2 - dd),0)) #Calculate horizontal range (in pixels) using pathagorean theorem
|
|
963
|
+
# Discard out-of-bounds indices
|
|
964
|
+
valid = src_idx < H_out
|
|
965
|
+
rows_v = row_arr[valid]
|
|
966
|
+
sidx_v = src_idx[valid]
|
|
969
967
|
|
|
970
|
-
|
|
968
|
+
pingDat = np.full(H_out, np.nan, dtype=np.float32)
|
|
969
|
+
if len(sidx_v) > 0:
|
|
970
|
+
data_extent = int(sidx_v[-1])
|
|
971
|
+
pingDat[sidx_v] = self.sonDat[rows_v, j].astype(np.float32)
|
|
972
|
+
pingDat[data_extent:] = 0 # Zero out values past range extent
|
|
971
973
|
|
|
972
|
-
|
|
973
|
-
dataExtent = srcIndex # Store range extent (max range) of ping
|
|
974
|
-
else:
|
|
975
|
-
pass
|
|
976
|
-
pingDat[dataExtent:]=0 # Zero out values past range extent so we don't interpolate past this
|
|
977
|
-
|
|
978
|
-
# # Process of relocating bed pixels will introduce across track gaps
|
|
979
|
-
# ## in the array so we will interpolate over gaps to fill them.
|
|
980
|
-
# nans, x = np.isnan(pingDat), lambda z: z.nonzero()[0]
|
|
981
|
-
# pingDat[nans] = np.interp(x(nans), x(~nans), pingDat[~nans])
|
|
974
|
+
pingDat[:nadirGap] = np.nan # Remove relocated water column
|
|
982
975
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
# Find where firs non-zero pixel is
|
|
987
|
-
nonZero = np.where(pingDat>0)[0]
|
|
988
|
-
print(pingDat[nonZero])
|
|
989
|
-
|
|
990
|
-
# Interpolate over gaps past nonZero
|
|
976
|
+
# Interpolate over gaps starting from first non-zero pixel
|
|
977
|
+
nonZero = np.where(pingDat > 0)[0]
|
|
991
978
|
if len(nonZero) > 0:
|
|
992
979
|
firstNonZero = nonZero[0]
|
|
993
|
-
|
|
994
|
-
|
|
980
|
+
tail = pingDat[firstNonZero:]
|
|
981
|
+
nans = np.isnan(tail)
|
|
982
|
+
if nans.any():
|
|
983
|
+
x = np.arange(len(tail))
|
|
984
|
+
tail[nans] = np.interp(x[nans], x[~nans], tail[~nans])
|
|
985
|
+
pingDat[firstNonZero:] = tail
|
|
995
986
|
|
|
996
987
|
# Store relocated ping in output array
|
|
997
988
|
if son:
|
|
998
|
-
srcDat[:,j] = np.around(pingDat, 0)
|
|
989
|
+
srcDat[:, j] = np.around(pingDat, 0)
|
|
999
990
|
else:
|
|
1000
|
-
srcDat[:,j] = pingDat
|
|
1001
|
-
|
|
1002
|
-
del pingDat
|
|
991
|
+
srcDat[:, j] = pingDat
|
|
1003
992
|
|
|
1004
993
|
if son:
|
|
1005
|
-
self.sonDat = srcDat.astype(int)
|
|
994
|
+
self.sonDat = srcDat.astype(int) # Store in class attribute for later use
|
|
1006
995
|
else:
|
|
1007
996
|
self.sonDat = srcDat
|
|
1008
997
|
del srcDat
|
|
@@ -266,6 +266,7 @@ def doWork(
|
|
|
266
266
|
'inFile': in_file,
|
|
267
267
|
'sonFiles': son_files,
|
|
268
268
|
'projDir': proj_dir,
|
|
269
|
+
'return_context': True,
|
|
269
270
|
})
|
|
270
271
|
|
|
271
272
|
print('\n\n', '***User Parameters***')
|
|
@@ -281,7 +282,13 @@ def doWork(
|
|
|
281
282
|
print('\n===========================================')
|
|
282
283
|
print('===========================================')
|
|
283
284
|
print('***** READING *****')
|
|
284
|
-
|
|
285
|
+
read_ctx = read_master_func(**run_params)
|
|
286
|
+
if isinstance(read_ctx, dict):
|
|
287
|
+
ss_chan_avail = bool(read_ctx.get('has_sidescan', False))
|
|
288
|
+
nav_available = bool(read_ctx.get('has_nav', True))
|
|
289
|
+
else:
|
|
290
|
+
ss_chan_avail = bool(read_ctx)
|
|
291
|
+
nav_available = True
|
|
285
292
|
|
|
286
293
|
if ss_chan_avail:
|
|
287
294
|
rect_wcp = run_params.get('rect_wcp', False)
|
|
@@ -293,6 +300,19 @@ def doWork(
|
|
|
293
300
|
export_poly = run_params.get('export_poly', False)
|
|
294
301
|
plt_subclass = run_params.get('pltSubClass', False)
|
|
295
302
|
|
|
303
|
+
if not nav_available:
|
|
304
|
+
if rect_wcp or rect_wcr or banklines or coverage or pred_sub or map_sub or export_poly or plt_subclass:
|
|
305
|
+
print('\nWARNING: Navigation info is unavailable for this recording.')
|
|
306
|
+
print('Skipping rectification and substrate mapping workflows (non-georeferenced sonogram-only processing).')
|
|
307
|
+
rect_wcp = False
|
|
308
|
+
rect_wcr = False
|
|
309
|
+
banklines = False
|
|
310
|
+
coverage = False
|
|
311
|
+
pred_sub = False
|
|
312
|
+
map_sub = False
|
|
313
|
+
export_poly = False
|
|
314
|
+
plt_subclass = False
|
|
315
|
+
|
|
296
316
|
if rect_wcp or rect_wcr or banklines or coverage or pred_sub or map_sub or export_poly:
|
|
297
317
|
print('\n===========================================')
|
|
298
318
|
print('===========================================')
|
|
@@ -162,6 +162,12 @@ def quiet_tensorflow_warnings():
|
|
|
162
162
|
tf.autograph.set_verbosity(0)
|
|
163
163
|
except Exception:
|
|
164
164
|
pass
|
|
165
|
+
try:
|
|
166
|
+
# Suppress Keras per-batch/per-step progress bar output
|
|
167
|
+
# (e.g. "1/1 [==============================] - 1s 1s/step")
|
|
168
|
+
tf.keras.utils.disable_interactive_logging()
|
|
169
|
+
except Exception:
|
|
170
|
+
pass
|
|
165
171
|
except Exception:
|
|
166
172
|
pass
|
|
167
173
|
|
|
@@ -42,6 +42,14 @@ from pingmapper.funcs_common import *
|
|
|
42
42
|
quiet_tensorflow_warnings()
|
|
43
43
|
import json
|
|
44
44
|
import numpy as np
|
|
45
|
+
|
|
46
|
+
# Prevent the transformers/huggingface_hub libraries from making network calls
|
|
47
|
+
# to huggingface.co to check for model updates. The model weights are stored
|
|
48
|
+
# locally, so no internet access is needed. These flags can also be set as
|
|
49
|
+
# system environment variables (TRANSFORMERS_OFFLINE=1, HF_HUB_OFFLINE=1)
|
|
50
|
+
# before launching PINGMapper to achieve the same effect without a code change.
|
|
51
|
+
os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
|
|
52
|
+
os.environ.setdefault("HF_HUB_OFFLINE", "1")
|
|
45
53
|
# import tensorflow as tf
|
|
46
54
|
# import tensorflow.keras.backend as K
|
|
47
55
|
# from tensorflow.python.client import device_lib
|
|
@@ -114,6 +122,47 @@ Utilities provided courtesy Dr. Dan Buscombe from segmentation_gym
|
|
|
114
122
|
https://github.com/Doodleverse/segmentation_gym
|
|
115
123
|
'''
|
|
116
124
|
|
|
125
|
+
#=======================================================================
|
|
126
|
+
def _build_segformer_from_config(id2label, num_classes, num_channels=3):
|
|
127
|
+
'''
|
|
128
|
+
Construct a SegFormer model (nvidia/mit-b0 backbone architecture) entirely
|
|
129
|
+
from a hardcoded local config, without contacting HuggingFace Hub.
|
|
130
|
+
|
|
131
|
+
The config values below are the canonical mit-b0 architecture spec.
|
|
132
|
+
The backbone weights loaded here are random initialisation — they are
|
|
133
|
+
immediately overwritten by model.load_weights() with the Zenodo-hosted
|
|
134
|
+
fine-tuned weights, so no HF-sourced weights are ever used.
|
|
135
|
+
'''
|
|
136
|
+
from transformers import TFSegformerForSemanticSegmentation, SegformerConfig
|
|
137
|
+
|
|
138
|
+
label2id = {label: id for id, label in id2label.items()}
|
|
139
|
+
|
|
140
|
+
config = SegformerConfig(
|
|
141
|
+
num_channels=num_channels,
|
|
142
|
+
num_encoder_blocks=4,
|
|
143
|
+
depths=[2, 2, 2, 2],
|
|
144
|
+
sr_ratios=[8, 4, 2, 1],
|
|
145
|
+
hidden_sizes=[32, 64, 160, 256],
|
|
146
|
+
patch_sizes=[7, 3, 3, 3],
|
|
147
|
+
strides=[4, 2, 2, 2],
|
|
148
|
+
num_attention_heads=[1, 2, 5, 8],
|
|
149
|
+
mlp_ratios=[4, 4, 4, 4],
|
|
150
|
+
hidden_act='gelu',
|
|
151
|
+
hidden_dropout_prob=0.0,
|
|
152
|
+
attention_probs_dropout_prob=0.0,
|
|
153
|
+
classifier_dropout_prob=0.1,
|
|
154
|
+
initializer_range=0.02,
|
|
155
|
+
drop_path_rate=0.1,
|
|
156
|
+
layer_norm_eps=1e-06,
|
|
157
|
+
decoder_hidden_size=256,
|
|
158
|
+
semantic_loss_ignore_index=255,
|
|
159
|
+
num_labels=num_classes,
|
|
160
|
+
id2label=id2label,
|
|
161
|
+
label2id=label2id,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
return TFSegformerForSemanticSegmentation(config)
|
|
165
|
+
|
|
117
166
|
#=======================================================================
|
|
118
167
|
def initModel(weights, configfile, USE_GPU=False):
|
|
119
168
|
'''
|
|
@@ -192,10 +241,21 @@ def initModel(weights, configfile, USE_GPU=False):
|
|
|
192
241
|
id2label = {}
|
|
193
242
|
for k in range(NCLASSES):
|
|
194
243
|
id2label[k]=str(k)
|
|
195
|
-
|
|
244
|
+
# SegFormer always uses 3 input channels: seg_file2tensor() converts
|
|
245
|
+
# single-channel images to 3-channel via np.dstack before inference,
|
|
246
|
+
# so the Zenodo weights are always saved with num_channels=3.
|
|
247
|
+
model = _build_segformer_from_config(id2label, NCLASSES, num_channels=3)
|
|
248
|
+
# Subclassed Keras models are lazy — variables don't exist until the
|
|
249
|
+
# first forward pass. Do a dummy call to build them so load_weights()
|
|
250
|
+
# can match the saved HDF5 file's variable names.
|
|
251
|
+
# Input to TFSegformer is NCHW: (batch, channels, height, width)
|
|
252
|
+
dummy = tf.zeros((1, 3, TARGET_SIZE[0], TARGET_SIZE[1]))
|
|
253
|
+
model(dummy, training=False)
|
|
196
254
|
|
|
197
255
|
model.load_weights(weights)
|
|
198
|
-
#
|
|
256
|
+
# Compile once here so doPredict never needs to recompile between chunks.
|
|
257
|
+
# compile_models mutates the model in-place; we discard the returned list.
|
|
258
|
+
compile_models([model], MODEL)
|
|
199
259
|
|
|
200
260
|
return model, MODEL, N_DATA_BANDS
|
|
201
261
|
|
|
@@ -209,7 +269,8 @@ def doPredict(model, MODEL, arr, N_DATA_BANDS, NCLASSES, TARGET_SIZE, OTSU_THRES
|
|
|
209
269
|
'''
|
|
210
270
|
'''
|
|
211
271
|
|
|
212
|
-
|
|
272
|
+
# Model is compiled once in initModel; just normalise to a list here.
|
|
273
|
+
model = [model[0]]
|
|
213
274
|
|
|
214
275
|
# Read array into a cropped and resized tensor
|
|
215
276
|
image, w, h, bigimage = seg_file2tensor(arr, N_DATA_BANDS, TARGET_SIZE, MODEL)
|
|
@@ -107,7 +107,10 @@ def map_master_func(logfilename='',
|
|
|
107
107
|
mosaic_nchunk=50,
|
|
108
108
|
mosaic=False,
|
|
109
109
|
map_mosaic=0,
|
|
110
|
-
banklines=False
|
|
110
|
+
banklines=False,
|
|
111
|
+
export_16bit=False,
|
|
112
|
+
export_16bit_colormap=False,
|
|
113
|
+
export_colormap_uint8=True):
|
|
111
114
|
|
|
112
115
|
'''
|
|
113
116
|
Main script to map substrates from side scan sonar imagery.
|
|
@@ -182,6 +185,15 @@ def map_master_func(logfilename='',
|
|
|
182
185
|
pass # Don't add non-port/star objects since they can't be rectified
|
|
183
186
|
del son, beam, sonObjs
|
|
184
187
|
|
|
188
|
+
if len(mapObjs) == 0:
|
|
189
|
+
print("\nNo side-scan channels available for substrate mapping. Skipping mapping.")
|
|
190
|
+
return
|
|
191
|
+
|
|
192
|
+
nav_available = all(getattr(son, 'trans', None) is not None for son in mapObjs)
|
|
193
|
+
if not nav_available:
|
|
194
|
+
print("\nNavigation info unavailable for side-scan channels. Skipping substrate mapping.")
|
|
195
|
+
return
|
|
196
|
+
|
|
185
197
|
################################################
|
|
186
198
|
# Prepare output directory and update attributes
|
|
187
199
|
for son in mapObjs:
|
|
@@ -399,6 +411,9 @@ def map_master_func(logfilename='',
|
|
|
399
411
|
# Create portstarObj
|
|
400
412
|
psObj = portstarObj(mapObjs)
|
|
401
413
|
|
|
414
|
+
# Pre-load CSVs once so each joblib-serialised copy already has the data
|
|
415
|
+
psObj._preloadRectifyCache()
|
|
416
|
+
|
|
402
417
|
Parallel(n_jobs=safe_n_jobs(len(toMap), threadCnt))(delayed(psObj._mapSubstrate)(map_class_method, c, f) for c, f in tqdm(toMap.items()))
|
|
403
418
|
|
|
404
419
|
del toMap
|
|
@@ -157,7 +157,8 @@ def read_master_func(logfilename='',
|
|
|
157
157
|
mosaic_nchunk=50,
|
|
158
158
|
mosaic=False,
|
|
159
159
|
map_mosaic=0,
|
|
160
|
-
banklines=False
|
|
160
|
+
banklines=False,
|
|
161
|
+
return_context=False):
|
|
161
162
|
|
|
162
163
|
'''
|
|
163
164
|
Main script to read data from Humminbird sonar recordings. Scripts have been
|
|
@@ -303,28 +304,7 @@ def read_master_func(logfilename='',
|
|
|
303
304
|
modelDir = get_segmentation_model_dir()
|
|
304
305
|
if not os.path.exists(modelDir):
|
|
305
306
|
downloadSegmentationModelsv1_0(modelDir)
|
|
306
|
-
|
|
307
|
-
else:
|
|
308
|
-
getSegformer = False
|
|
309
|
-
|
|
310
|
-
###############
|
|
311
|
-
# Get segformer
|
|
312
|
-
if getSegformer:
|
|
313
|
-
NCLASSES = 8
|
|
314
|
-
id2label = {}
|
|
315
|
-
for k in range(NCLASSES):
|
|
316
|
-
id2label[k] = str(k)
|
|
317
|
-
|
|
318
|
-
# try downloading segformer pretrained model
|
|
319
|
-
try:
|
|
320
|
-
_ = segformer(id2label, NCLASSES)
|
|
321
|
-
except:
|
|
322
|
-
print('\n\n\n\n')
|
|
323
|
-
print('ERROR! Unable to download pretrained SegFormer model!')
|
|
324
|
-
print('Your network settings are blocking the download.')
|
|
325
|
-
print('Please try running on a different network.')
|
|
326
|
-
print('Once the script has been run successfully, you should be able')
|
|
327
|
-
print('to run PING-Mapper on the current network.')
|
|
307
|
+
|
|
328
308
|
|
|
329
309
|
|
|
330
310
|
###############################################
|
|
@@ -393,6 +373,27 @@ def read_master_func(logfilename='',
|
|
|
393
373
|
print('\n\nERROR!\n\nFile type {} not supported at this time.'.format(file_type))
|
|
394
374
|
sys.exit()
|
|
395
375
|
|
|
376
|
+
nav_available = bool(getattr(sonar_obj, 'has_position', True))
|
|
377
|
+
|
|
378
|
+
# Sonar-only fallback for sources without navigation fields (e.g., some Cerulean logs).
|
|
379
|
+
# Disable filters that rely on geospatial motion/position so processing can continue.
|
|
380
|
+
if getattr(sonar_obj, 'has_position', True) is False:
|
|
381
|
+
nav_filters_requested = (
|
|
382
|
+
(max_heading_deviation > 0) or
|
|
383
|
+
(min_speed > 0) or
|
|
384
|
+
(max_speed > 0) or
|
|
385
|
+
bool(aoi)
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
if nav_filters_requested:
|
|
389
|
+
print('\nWARNING: Navigation fields are unavailable for this recording (sonar-only mode).')
|
|
390
|
+
print('Disabling nav-dependent filters: max_heading_deviation, min_speed, max_speed, aoi.')
|
|
391
|
+
|
|
392
|
+
max_heading_deviation = 0
|
|
393
|
+
min_speed = 0
|
|
394
|
+
max_speed = 0
|
|
395
|
+
aoi = False
|
|
396
|
+
|
|
396
397
|
####################
|
|
397
398
|
# Create son objects
|
|
398
399
|
####################
|
|
@@ -1102,29 +1103,41 @@ def read_master_func(logfilename='',
|
|
|
1102
1103
|
if _is_sidescan_beam(beam):
|
|
1103
1104
|
portstar.append(son)
|
|
1104
1105
|
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
chunks = []
|
|
1109
|
-
for son in portstar:
|
|
1110
|
-
# Get chunk id's, ignoring those with nodata
|
|
1111
|
-
c = son._getChunkID()
|
|
1112
|
-
|
|
1113
|
-
chunks.extend(c)
|
|
1114
|
-
del c
|
|
1115
|
-
del son
|
|
1116
|
-
|
|
1117
|
-
chunks = np.unique(chunks).astype(int)
|
|
1106
|
+
psObj = None
|
|
1107
|
+
chunks = np.array([], dtype=int)
|
|
1118
1108
|
|
|
1119
|
-
if len(
|
|
1109
|
+
if len(portstar) == 0:
|
|
1120
1110
|
print(
|
|
1121
|
-
'\n\nNo
|
|
1111
|
+
'\n\nNo recognized side-scan channels available for depth processing. '\
|
|
1122
1112
|
'Continuing with down-looking beams only.'
|
|
1123
1113
|
)
|
|
1124
1114
|
print('Disabling side-scan-only operations (auto depth, bedpick plot, shadow removal).')
|
|
1125
1115
|
detectDep = 0
|
|
1126
1116
|
pltBedPick = False
|
|
1127
1117
|
remShadow = 0
|
|
1118
|
+
else:
|
|
1119
|
+
# Create portstarObj
|
|
1120
|
+
psObj = portstarObj(portstar)
|
|
1121
|
+
|
|
1122
|
+
chunks = []
|
|
1123
|
+
for son in portstar:
|
|
1124
|
+
# Get chunk id's, ignoring those with nodata
|
|
1125
|
+
c = son._getChunkID()
|
|
1126
|
+
|
|
1127
|
+
chunks.extend(c)
|
|
1128
|
+
del c
|
|
1129
|
+
|
|
1130
|
+
chunks = np.unique(chunks).astype(int)
|
|
1131
|
+
|
|
1132
|
+
if len(chunks) == 0:
|
|
1133
|
+
print(
|
|
1134
|
+
'\n\nNo valid side-scan chunks available for depth processing. '\
|
|
1135
|
+
'Continuing with down-looking beams only.'
|
|
1136
|
+
)
|
|
1137
|
+
print('Disabling side-scan-only operations (auto depth, bedpick plot, shadow removal).')
|
|
1138
|
+
detectDep = 0
|
|
1139
|
+
pltBedPick = False
|
|
1140
|
+
remShadow = 0
|
|
1128
1141
|
|
|
1129
1142
|
# # Automatically estimate depth
|
|
1130
1143
|
if detectDep > 0:
|
|
@@ -1187,7 +1200,7 @@ def read_master_func(logfilename='',
|
|
|
1187
1200
|
|
|
1188
1201
|
if saveDepth:
|
|
1189
1202
|
|
|
1190
|
-
if ss_chan_avail:
|
|
1203
|
+
if ss_chan_avail and psObj is not None:
|
|
1191
1204
|
# Save detected depth to csv
|
|
1192
1205
|
depDF = psObj._saveDepth(chunks, detectDep, smthDep, adjDep, instDepAvail)
|
|
1193
1206
|
else:
|
|
@@ -1246,14 +1259,15 @@ def read_master_func(logfilename='',
|
|
|
1246
1259
|
del depDF
|
|
1247
1260
|
|
|
1248
1261
|
# Cleanup
|
|
1249
|
-
psObj
|
|
1262
|
+
if psObj is not None:
|
|
1263
|
+
psObj._cleanup()
|
|
1250
1264
|
|
|
1251
1265
|
print("\nDone!")
|
|
1252
1266
|
print("Time (s):", round(time.time() - start_time, ndigits=1))
|
|
1253
1267
|
printUsage()
|
|
1254
1268
|
|
|
1255
1269
|
# Plot sonar depth and auto depth estimate (if available) on sonogram
|
|
1256
|
-
if pltBedPick:
|
|
1270
|
+
if pltBedPick and psObj is not None and len(chunks) > 0:
|
|
1257
1271
|
start_time = time.time()
|
|
1258
1272
|
|
|
1259
1273
|
print("\n\nExporting bedpick plots to {}...".format(tileFile))
|
|
@@ -1264,7 +1278,8 @@ def read_master_func(logfilename='',
|
|
|
1264
1278
|
printUsage()
|
|
1265
1279
|
|
|
1266
1280
|
# Cleanup
|
|
1267
|
-
psObj
|
|
1281
|
+
if psObj is not None:
|
|
1282
|
+
psObj._cleanup()
|
|
1268
1283
|
del psObj, portstar
|
|
1269
1284
|
|
|
1270
1285
|
for son in sonObjs:
|
|
@@ -1638,8 +1653,13 @@ def read_master_func(logfilename='',
|
|
|
1638
1653
|
gc.collect()
|
|
1639
1654
|
printUsage()
|
|
1640
1655
|
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
return
|
|
1656
|
+
has_sidescan = len(ss_chan_avail) > 0
|
|
1657
|
+
|
|
1658
|
+
if return_context:
|
|
1659
|
+
return {
|
|
1660
|
+
'has_sidescan': has_sidescan,
|
|
1661
|
+
'has_nav': nav_available,
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
return has_sidescan
|
|
1645
1665
|
|
|
@@ -307,6 +307,15 @@ def rectify_master_func(logfilename='',
|
|
|
307
307
|
pass # Don't add non-port/star objects since they can't be rectified
|
|
308
308
|
del son, beam, rectObjs
|
|
309
309
|
|
|
310
|
+
if len(portstar) == 0:
|
|
311
|
+
print("\nNo side-scan channels available for rectification. Skipping rectification.")
|
|
312
|
+
return
|
|
313
|
+
|
|
314
|
+
nav_available = all(getattr(son, 'trans', None) is not None for son in portstar)
|
|
315
|
+
if not nav_available:
|
|
316
|
+
print("\nNavigation info unavailable for side-scan channels. Skipping rectification.")
|
|
317
|
+
return
|
|
318
|
+
|
|
310
319
|
############################################################################
|
|
311
320
|
# Smooth Trackline #
|
|
312
321
|
############################################################################
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '5.3.2'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pingmapper
|
|
3
|
-
Version: 5.3.
|
|
3
|
+
Version: 5.3.2
|
|
4
4
|
Summary: Open-source interface for processing recreation-grade side scan sonar datasets and reproducibly mapping benthic habitat
|
|
5
5
|
Author: Daniel Buscombe
|
|
6
6
|
Author-email: Cameron Bodine <bodine.cs@gmail.email>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '5.3.0'
|
|
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.3.0 → pingmapper-5.3.2}/pingmapper/utils/DRAFT_Workflows/avg_predictions_Mussel_WBL.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pingmapper-5.3.0 → pingmapper-5.3.2}/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.3.0 → pingmapper-5.3.2}/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
|