pingmapper 5.3.4__tar.gz → 5.3.6__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.4 → pingmapper-5.3.6}/PKG-INFO +1 -1
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/class_portstarObj.py +75 -30
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/class_sonObj.py +2 -2
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/class_sonObj_nadirgaptest.py +2 -2
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/doWork.py +4 -2
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/funcs_common.py +24 -3
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/gui_main.py +2 -5
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/main_mapSubstrate.py +2 -1
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/main_readFiles.py +103 -60
- pingmapper-5.3.6/pingmapper/version.py +1 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper.egg-info/PKG-INFO +1 -1
- pingmapper-5.3.4/pingmapper/version.py +0 -1
- {pingmapper-5.3.4 → pingmapper-5.3.6}/LICENSE +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/README.md +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/__init__.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/__main__.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/class_mapSubstrateObj.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/class_rectObj.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/default_params.json +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/funcs_model.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/funcs_rectify.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/main_rectify.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/processing_scripts/main_batchDirectory_2024-01-18_0926.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/processing_scripts/main_batchDirectory_2024-01-18_0929.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/scratch/funcs_pyhum_correct.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/scratch/main.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/scratch/main_batchDirectory.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/test_PINGMapper.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/test_time.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/DRAFT_Workflows/avg_predictions_Mussel_WBL.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/DRAFT_Workflows/gen_centerline.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/DRAFT_Workflows/gen_centerline_from_bankline.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/DRAFT_Workflows/gen_centerline_trkpnts_fitspline_DRAFT.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/DRAFT_Workflows/testEXAMPLE_mosaic_logit.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/RawEGN_avg_predictions.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/Substrate_Summaries/00_substrate_logits_mosaic_transects.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/Substrate_Summaries/00_substrate_shps_mosaic_transects.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/Substrate_Summaries/01_gen_centerline_from_coverage.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/Substrate_Summaries/02_gen_summary_stamp_shps.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/Substrate_Summaries/03_gen_summary_shp.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/Substrate_Summaries/04_combine_summary_shp_csv.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/Substrate_Summaries/05_gen_summary_shp_plots.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/Substrate_Summaries/06_compare_raw-egn_volume.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/Substrate_Summaries/08_raw-egn_hardReacheFreq_hist.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/Substrate_Summaries/09_raw-egn_PatchSize_density.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/Substrate_Summaries/summarize_project_substrate.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/export_coverage.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper/utils/main_mosaic_transects.py +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper.egg-info/SOURCES.txt +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper.egg-info/dependency_links.txt +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper.egg-info/requires.txt +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pingmapper.egg-info/top_level.txt +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/pyproject.toml +0 -0
- {pingmapper-5.3.4 → pingmapper-5.3.6}/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.6
|
|
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>
|
|
@@ -979,20 +979,20 @@ class portstarObj(object):
|
|
|
979
979
|
self._depthZheng() or self._depthThreshold()
|
|
980
980
|
'''
|
|
981
981
|
|
|
982
|
-
# Check if depth detection dependencies are available
|
|
983
|
-
if not DEPTH_DETECTION_AVAILABLE:
|
|
984
|
-
raise ImportError(
|
|
985
|
-
"TensorFlow, Transformers, and/or Doodleverse Utils are not installed. "
|
|
986
|
-
"These packages are required for automatic depth detection. "
|
|
987
|
-
"Please install them using: pip install tensorflow transformers doodleverse-utils"
|
|
988
|
-
)
|
|
989
|
-
|
|
990
|
-
# Open model configuration file
|
|
991
|
-
with open(self.configfile) as f:
|
|
992
|
-
config = json.load(f)
|
|
993
|
-
globals().update(config)
|
|
994
|
-
|
|
995
982
|
if method == 1:
|
|
983
|
+
# Check if depth detection dependencies are available
|
|
984
|
+
if not DEPTH_DETECTION_AVAILABLE:
|
|
985
|
+
raise ImportError(
|
|
986
|
+
"TensorFlow, Transformers, and/or Doodleverse Utils are not installed. "
|
|
987
|
+
"These packages are required for ML depth detection. "
|
|
988
|
+
"Use method=2 for binary-threshold depth picking instead."
|
|
989
|
+
)
|
|
990
|
+
|
|
991
|
+
# Open model configuration file
|
|
992
|
+
with open(self.configfile) as f:
|
|
993
|
+
config = json.load(f)
|
|
994
|
+
globals().update(config)
|
|
995
|
+
|
|
996
996
|
if not hasattr(self, 'bedpickModel'):
|
|
997
997
|
# model = self._initModel(USE_GPU)
|
|
998
998
|
model = initModel(self.weights, self.configfile, USE_GPU)
|
|
@@ -1003,7 +1003,7 @@ class portstarObj(object):
|
|
|
1003
1003
|
elif method == 2:
|
|
1004
1004
|
self.port._loadSonMeta()
|
|
1005
1005
|
self.star._loadSonMeta()
|
|
1006
|
-
portDepPixCrop, starDepPixCrop, i = self._depthThreshold(i
|
|
1006
|
+
portDepPixCrop, starDepPixCrop, i = self._depthThreshold(i)
|
|
1007
1007
|
|
|
1008
1008
|
gc.collect()
|
|
1009
1009
|
return portDepPixCrop, starDepPixCrop, i
|
|
@@ -1391,25 +1391,31 @@ class portstarObj(object):
|
|
|
1391
1391
|
for son in portstar:
|
|
1392
1392
|
# Load sonar intensity, standardize & rescale
|
|
1393
1393
|
son._getScanChunkSingle(chunk)
|
|
1394
|
-
img = son.sonDat
|
|
1395
|
-
|
|
1394
|
+
img = standardize(np.asarray(son.sonDat)).squeeze()
|
|
1395
|
+
if img.ndim == 3:
|
|
1396
|
+
img = img[:, :, -1]
|
|
1396
1397
|
W, H = img.shape[1], img.shape[0]
|
|
1397
1398
|
|
|
1398
1399
|
# Get chunks sonar metadata and instrument depth
|
|
1399
|
-
isChunk = son.sonMetaDF['chunk_id']==
|
|
1400
|
+
isChunk = son.sonMetaDF['chunk_id'] == chunk
|
|
1400
1401
|
sonMeta = son.sonMetaDF[isChunk].reset_index()
|
|
1401
|
-
|
|
1402
|
-
acousticBed = round(
|
|
1402
|
+
acoustic_depth = pd.to_numeric(sonMeta['inst_dep_m'], errors='coerce').to_numpy(dtype=float, copy=True)
|
|
1403
|
+
acousticBed = np.round(acoustic_depth / sonMeta['pixM'].to_numpy(dtype=float, copy=True), 0)
|
|
1404
|
+
acousticBed = acousticBed[np.isfinite(acousticBed) & (acousticBed > 0)]
|
|
1403
1405
|
|
|
1404
1406
|
##################################
|
|
1405
1407
|
# Step 1 : Acoustic Bedpick Filter
|
|
1406
1408
|
# Use acoustic bed pick to crop image
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
+
if acousticBed.size > 0:
|
|
1410
|
+
bedMin = max(int(np.nanmin(acousticBed)) - 50, 0)
|
|
1411
|
+
bedMax = int(np.nanmax(acousticBed)) + pix_buf
|
|
1412
|
+
else:
|
|
1413
|
+
bedMin = 0
|
|
1414
|
+
bedMax = H
|
|
1409
1415
|
|
|
1410
1416
|
cropMask = np.ones((H, W)).astype(int)
|
|
1411
1417
|
cropMask[:bedMin,:] = 0
|
|
1412
|
-
cropMask[bedMax:,:] = 0
|
|
1418
|
+
cropMask[min(bedMax, H):,:] = 0
|
|
1413
1419
|
|
|
1414
1420
|
# Mask the image with bed_mask
|
|
1415
1421
|
imgMasked = img*cropMask
|
|
@@ -1464,7 +1470,7 @@ class portstarObj(object):
|
|
|
1464
1470
|
imgBinaryMask[imgBinaryMask>0] = 1 # Now set all val's greater than 0 to 1 to create the mask
|
|
1465
1471
|
|
|
1466
1472
|
# Now fill in above last row filled to make sure no gaps in bed pixels
|
|
1467
|
-
lastRow = bedMax
|
|
1473
|
+
lastRow = min(bedMax, H - 1)
|
|
1468
1474
|
imgBinaryMask[lastRow] = True
|
|
1469
1475
|
for i in range(W):
|
|
1470
1476
|
if imgBinaryMask[lastRow-1,i] == 0:
|
|
@@ -1509,10 +1515,10 @@ class portstarObj(object):
|
|
|
1509
1515
|
bed[nans] = np.interp(x(nans), x(~nans), bed[~nans])
|
|
1510
1516
|
bed = bed.astype(int)
|
|
1511
1517
|
|
|
1512
|
-
if son.beamName
|
|
1518
|
+
if str(son.beamName).startswith('ss_port'):
|
|
1513
1519
|
# self.portDepDetect[chunk] = bed
|
|
1514
1520
|
portDepPixCrop = bed
|
|
1515
|
-
elif son.beamName
|
|
1521
|
+
elif str(son.beamName).startswith('ss_star'):
|
|
1516
1522
|
# self.starDepDetect[chunk] = bed
|
|
1517
1523
|
starDepPixCrop = bed
|
|
1518
1524
|
|
|
@@ -1557,6 +1563,43 @@ class portstarObj(object):
|
|
|
1557
1563
|
Integer < 0 = decrease depth estimate by x pixels.
|
|
1558
1564
|
0 = use depth estimate with no adjustment.
|
|
1559
1565
|
'''
|
|
1566
|
+
def _format_depth_adjustment(pix_m_series):
|
|
1567
|
+
pix_m = pd.to_numeric(pix_m_series, errors='coerce')
|
|
1568
|
+
valid_pix = pix_m[np.isfinite(pix_m) & (pix_m > 0)]
|
|
1569
|
+
if len(valid_pix) == 0:
|
|
1570
|
+
return '0 pixels'
|
|
1571
|
+
return str(float(adjDep) / float(valid_pix.iloc[0])) + ' pixels'
|
|
1572
|
+
|
|
1573
|
+
def _sync_trackline_depth(beam_obj, beam_df):
|
|
1574
|
+
trk_file = os.path.join(beam_obj.metaDir, 'Trackline_Smth_' + beam_obj.beamName + '.csv')
|
|
1575
|
+
if not os.path.exists(trk_file):
|
|
1576
|
+
return
|
|
1577
|
+
|
|
1578
|
+
trk_df = pd.read_csv(trk_file)
|
|
1579
|
+
if 'record_num' not in trk_df.columns or 'record_num' not in beam_df.columns:
|
|
1580
|
+
return
|
|
1581
|
+
|
|
1582
|
+
depth_cols = ['record_num', 'dep_m']
|
|
1583
|
+
if 'dep_m_Method' in beam_df.columns:
|
|
1584
|
+
depth_cols.append('dep_m_Method')
|
|
1585
|
+
if 'dep_m_smth' in beam_df.columns:
|
|
1586
|
+
depth_cols.append('dep_m_smth')
|
|
1587
|
+
if 'dep_m_adjBy' in beam_df.columns:
|
|
1588
|
+
depth_cols.append('dep_m_adjBy')
|
|
1589
|
+
|
|
1590
|
+
depth_df = beam_df[depth_cols].drop_duplicates(subset=['record_num'], keep='last').set_index('record_num')
|
|
1591
|
+
trk_df = trk_df.set_index('record_num')
|
|
1592
|
+
|
|
1593
|
+
trk_df['dep_m'] = depth_df['dep_m']
|
|
1594
|
+
if 'dep_m_Method' in depth_df.columns:
|
|
1595
|
+
trk_df['dep_m_Method'] = depth_df['dep_m_Method']
|
|
1596
|
+
if 'dep_m_smth' in depth_df.columns:
|
|
1597
|
+
trk_df['dep_m_smth'] = depth_df['dep_m_smth']
|
|
1598
|
+
if 'dep_m_adjBy' in depth_df.columns:
|
|
1599
|
+
trk_df['dep_m_adjBy'] = depth_df['dep_m_adjBy']
|
|
1600
|
+
|
|
1601
|
+
trk_df.reset_index().to_csv(trk_file, index=False, float_format='%.14f')
|
|
1602
|
+
|
|
1560
1603
|
# Load sonar metadata file
|
|
1561
1604
|
self.port._loadSonMeta()
|
|
1562
1605
|
portDF = self.port.sonMetaDF
|
|
@@ -1617,8 +1660,8 @@ class portstarObj(object):
|
|
|
1617
1660
|
portDF['dep_m_smth'] = smthDep
|
|
1618
1661
|
starDF['dep_m_smth'] = smthDep
|
|
1619
1662
|
|
|
1620
|
-
portDF['dep_m_adjBy'] =
|
|
1621
|
-
starDF['dep_m_adjBy'] =
|
|
1663
|
+
portDF['dep_m_adjBy'] = _format_depth_adjustment(portDF['pixM'])
|
|
1664
|
+
starDF['dep_m_adjBy'] = _format_depth_adjustment(starDF['pixM'])
|
|
1622
1665
|
|
|
1623
1666
|
elif detectDep > 0:
|
|
1624
1667
|
# Prepare depth detection dictionaries
|
|
@@ -1703,8 +1746,8 @@ class portstarObj(object):
|
|
|
1703
1746
|
portDF['dep_m_smth'] = smthDep
|
|
1704
1747
|
starDF['dep_m_smth'] = smthDep
|
|
1705
1748
|
|
|
1706
|
-
portDF['dep_m_adjBy'] =
|
|
1707
|
-
starDF['dep_m_adjBy'] =
|
|
1749
|
+
portDF['dep_m_adjBy'] = _format_depth_adjustment(portDF['pixM'])
|
|
1750
|
+
starDF['dep_m_adjBy'] = _format_depth_adjustment(starDF['pixM'])
|
|
1708
1751
|
|
|
1709
1752
|
# Interpolate over nan's (and set zeros to nan)
|
|
1710
1753
|
portDep = portDF['dep_m'].to_numpy(copy=True)
|
|
@@ -1730,6 +1773,8 @@ class portstarObj(object):
|
|
|
1730
1773
|
# Export to csv
|
|
1731
1774
|
portDF.to_csv(self.port.sonMetaFile, index=False, float_format='%.14f')
|
|
1732
1775
|
starDF.to_csv(self.star.sonMetaFile, index=False, float_format='%.14f')
|
|
1776
|
+
_sync_trackline_depth(self.port, portDF)
|
|
1777
|
+
_sync_trackline_depth(self.star, starDF)
|
|
1733
1778
|
|
|
1734
1779
|
try:
|
|
1735
1780
|
# Take average of both estimates to store with downlooking sonar csv
|
|
@@ -2305,7 +2350,7 @@ class portstarObj(object):
|
|
|
2305
2350
|
dd = float(depth * depth)
|
|
2306
2351
|
row_arr = np.arange(depth, H_pred)
|
|
2307
2352
|
src_idx = np.round(
|
|
2308
|
-
np.sqrt(row_arr.astype(np.
|
|
2353
|
+
np.sqrt(row_arr.astype(np.float32) ** 2 - dd)
|
|
2309
2354
|
).astype(int)
|
|
2310
2355
|
valid = src_idx < H_pred
|
|
2311
2356
|
rows_v = row_arr[valid]
|
|
@@ -1328,7 +1328,7 @@ class sonObj(object):
|
|
|
1328
1328
|
row_arr = np.arange(depth, H)
|
|
1329
1329
|
# Horizontal range index via Pythagorean theorem (vectorised)
|
|
1330
1330
|
src_idx = np.round(
|
|
1331
|
-
np.sqrt(row_arr.astype(np.
|
|
1331
|
+
np.sqrt(row_arr.astype(np.float32) ** 2 - dd)
|
|
1332
1332
|
).astype(int)
|
|
1333
1333
|
|
|
1334
1334
|
# Discard out-of-bounds indices
|
|
@@ -2919,7 +2919,7 @@ class sonObj(object):
|
|
|
2919
2919
|
'''
|
|
2920
2920
|
|
|
2921
2921
|
# Get sonDat
|
|
2922
|
-
sonDat = self.sonDat.astype('
|
|
2922
|
+
sonDat = self.sonDat.astype('float32')
|
|
2923
2923
|
|
|
2924
2924
|
# Create mask from zero values
|
|
2925
2925
|
mask = np.where(sonDat == 0, 0, 1)
|
|
@@ -957,7 +957,7 @@ class sonObj(object):
|
|
|
957
957
|
row_arr = np.arange(depth, H)
|
|
958
958
|
# Horizontal range via Pythagorean theorem, plus nadir gap offset
|
|
959
959
|
src_idx = np.round(
|
|
960
|
-
np.sqrt(row_arr.astype(np.
|
|
960
|
+
np.sqrt(row_arr.astype(np.float32) ** 2 - dd)
|
|
961
961
|
).astype(int) + nadirGap
|
|
962
962
|
|
|
963
963
|
# Discard out-of-bounds indices
|
|
@@ -2356,7 +2356,7 @@ class sonObj(object):
|
|
|
2356
2356
|
'''
|
|
2357
2357
|
|
|
2358
2358
|
# Get sonDat
|
|
2359
|
-
sonDat = self.sonDat.astype('
|
|
2359
|
+
sonDat = self.sonDat.astype('float32')
|
|
2360
2360
|
|
|
2361
2361
|
# Create mask from zero values
|
|
2362
2362
|
mask = np.where(sonDat == 0, 0, 1)
|
|
@@ -293,6 +293,7 @@ def doWork(
|
|
|
293
293
|
if ss_chan_avail:
|
|
294
294
|
rect_wcp = run_params.get('rect_wcp', False)
|
|
295
295
|
rect_wcr = run_params.get('rect_wcr', False)
|
|
296
|
+
force_rectify = run_params.get('force_rectify', False)
|
|
296
297
|
banklines = run_params.get('banklines', False)
|
|
297
298
|
coverage = run_params.get('coverage', False)
|
|
298
299
|
pred_sub = run_params.get('pred_sub', False)
|
|
@@ -301,11 +302,12 @@ def doWork(
|
|
|
301
302
|
plt_subclass = run_params.get('pltSubClass', False)
|
|
302
303
|
|
|
303
304
|
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
|
+
if rect_wcp or rect_wcr or force_rectify or banklines or coverage or pred_sub or map_sub or export_poly or plt_subclass:
|
|
305
306
|
print('\nWARNING: Navigation info is unavailable for this recording.')
|
|
306
307
|
print('Skipping rectification and substrate mapping workflows (non-georeferenced sonogram-only processing).')
|
|
307
308
|
rect_wcp = False
|
|
308
309
|
rect_wcr = False
|
|
310
|
+
force_rectify = False
|
|
309
311
|
banklines = False
|
|
310
312
|
coverage = False
|
|
311
313
|
pred_sub = False
|
|
@@ -313,7 +315,7 @@ def doWork(
|
|
|
313
315
|
export_poly = False
|
|
314
316
|
plt_subclass = False
|
|
315
317
|
|
|
316
|
-
if rect_wcp or rect_wcr or banklines or coverage or pred_sub or map_sub or export_poly:
|
|
318
|
+
if rect_wcp or rect_wcr or force_rectify or banklines or coverage or pred_sub or map_sub or export_poly:
|
|
317
319
|
print('\n===========================================')
|
|
318
320
|
print('===========================================')
|
|
319
321
|
print('***** RECTIFYING *****')
|
|
@@ -31,6 +31,12 @@
|
|
|
31
31
|
|
|
32
32
|
import os, sys, struct, gc, io, contextlib
|
|
33
33
|
|
|
34
|
+
# Configure TensorFlow runtime defaults as early as possible so informational
|
|
35
|
+
# startup messages do not leak to stderr before logging is initialized.
|
|
36
|
+
os.environ.setdefault('TF_CPP_MIN_LOG_LEVEL', '3')
|
|
37
|
+
os.environ.setdefault('TF_ENABLE_ONEDNN_OPTS', '0')
|
|
38
|
+
os.environ.setdefault('AUTOGRAPH_VERBOSITY', '0')
|
|
39
|
+
|
|
34
40
|
# Add 'pingmapper' to the path, may not need after pypi package...
|
|
35
41
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
36
42
|
PACKAGE_DIR = os.path.dirname(SCRIPT_DIR)
|
|
@@ -97,13 +103,27 @@ import json
|
|
|
97
103
|
|
|
98
104
|
import logging
|
|
99
105
|
|
|
100
|
-
from tqdm import tqdm
|
|
106
|
+
from tqdm import tqdm as _tqdm
|
|
101
107
|
|
|
102
108
|
import subprocess
|
|
103
109
|
|
|
104
110
|
# from funcs_pyhum_correct import doPyhumCorrections
|
|
105
111
|
|
|
106
112
|
|
|
113
|
+
# =========================================================
|
|
114
|
+
def tqdm(*args, **kwargs):
|
|
115
|
+
'''
|
|
116
|
+
Shared tqdm wrapper so CLI progress bars stay readable in the GUI console.
|
|
117
|
+
Use PINGMAPPER_TQDM_NCOLS to override the default width when needed.
|
|
118
|
+
'''
|
|
119
|
+
if 'ncols' not in kwargs:
|
|
120
|
+
try:
|
|
121
|
+
kwargs['ncols'] = int(os.environ.get('PINGMAPPER_TQDM_NCOLS', '80'))
|
|
122
|
+
except (TypeError, ValueError):
|
|
123
|
+
kwargs['ncols'] = 80
|
|
124
|
+
return _tqdm(*args, **kwargs)
|
|
125
|
+
|
|
126
|
+
|
|
107
127
|
# =========================================================
|
|
108
128
|
def safe_n_jobs(task_count, thread_count=0):
|
|
109
129
|
'''
|
|
@@ -139,6 +159,7 @@ def quiet_tensorflow_warnings():
|
|
|
139
159
|
Safe to call even if TensorFlow is not installed.
|
|
140
160
|
'''
|
|
141
161
|
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
|
|
162
|
+
os.environ['TF_ENABLE_ONEDNN_OPTS'] = '0'
|
|
142
163
|
os.environ['AUTOGRAPH_VERBOSITY'] = '0'
|
|
143
164
|
|
|
144
165
|
logging.getLogger('tensorflow').setLevel(logging.ERROR)
|
|
@@ -443,7 +464,7 @@ def saveDefaultParams(values, user_params=None):
|
|
|
443
464
|
return {k: _sanitize(val) for k, val in v.items()}
|
|
444
465
|
if isinstance(v, (np.integer, np.int_, np.int64)):
|
|
445
466
|
return int(v)
|
|
446
|
-
if isinstance(v, (np.floating, np.
|
|
467
|
+
if isinstance(v, (np.floating, np.float32, np.float32)):
|
|
447
468
|
return float(v)
|
|
448
469
|
if isinstance(v, (np.ndarray, list, tuple)):
|
|
449
470
|
return [_sanitize(x) for x in v]
|
|
@@ -516,7 +537,7 @@ class FastPiecewiseAffineTransform(PiecewiseAffineTransform):
|
|
|
516
537
|
[self.affines[i].params for i in range(len(self._tesselation.simplices))]
|
|
517
538
|
)
|
|
518
539
|
|
|
519
|
-
result = np.empty((n, 3), dtype=np.
|
|
540
|
+
result = np.empty((n, 3), dtype=np.float32)
|
|
520
541
|
|
|
521
542
|
# Process in batches to avoid large allocations in find_simplex and the
|
|
522
543
|
# affine einsum, both of which scale with n_coords and can exceed several GiB.
|
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
import sys, os
|
|
3
3
|
import webbrowser
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import FreeSimpleGUI as sg
|
|
8
|
-
except ImportError:
|
|
9
|
-
import PySimpleGUI as sg
|
|
5
|
+
|
|
6
|
+
import FreeSimpleGUI as sg
|
|
10
7
|
import matplotlib.pyplot as plt
|
|
11
8
|
|
|
12
9
|
# Add 'pingmapper' to the path, may not need after pypi package...
|
|
@@ -110,7 +110,8 @@ def map_master_func(logfilename='',
|
|
|
110
110
|
banklines=False,
|
|
111
111
|
export_16bit=False,
|
|
112
112
|
export_16bit_colormap=False,
|
|
113
|
-
export_colormap_uint8=True
|
|
113
|
+
export_colormap_uint8=True,
|
|
114
|
+
**kwargs):
|
|
114
115
|
|
|
115
116
|
'''
|
|
116
117
|
Main script to map substrates from side scan sonar imagery.
|
|
@@ -94,6 +94,17 @@ def _is_sidescan_beam(beam_name):
|
|
|
94
94
|
return beam_name.startswith('ss_port') or beam_name.startswith('ss_star')
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
def _sidescan_group_key(beam_name):
|
|
98
|
+
beam_name = str(beam_name)
|
|
99
|
+
if beam_name.startswith('ss_port_'):
|
|
100
|
+
return beam_name[len('ss_port_'):]
|
|
101
|
+
if beam_name.startswith('ss_star_'):
|
|
102
|
+
return beam_name[len('ss_star_'):]
|
|
103
|
+
if beam_name in {'ss_port', 'ss_star'}:
|
|
104
|
+
return 'default'
|
|
105
|
+
return beam_name
|
|
106
|
+
|
|
107
|
+
|
|
97
108
|
#===========================================
|
|
98
109
|
def read_master_func(logfilename='',
|
|
99
110
|
project_mode=0,
|
|
@@ -158,7 +169,9 @@ def read_master_func(logfilename='',
|
|
|
158
169
|
mosaic=False,
|
|
159
170
|
map_mosaic=0,
|
|
160
171
|
banklines=False,
|
|
161
|
-
|
|
172
|
+
side_scan_only=False,
|
|
173
|
+
return_context=False,
|
|
174
|
+
**kwargs):
|
|
162
175
|
|
|
163
176
|
'''
|
|
164
177
|
Main script to read data from Humminbird sonar recordings. Scripts have been
|
|
@@ -353,7 +366,7 @@ def read_master_func(logfilename='',
|
|
|
353
366
|
# Prepare Cerulean file for PINGMapper
|
|
354
367
|
elif file_type == '.svlog':
|
|
355
368
|
sonar_obj = cerul2pingmapper(inFile, projDir, nchunk, tempC, exportUnknown)
|
|
356
|
-
detectDep = 1
|
|
369
|
+
detectDep = 1 if DEPTH_DETECTION_AVAILABLE else 2
|
|
357
370
|
instDepAvail = False
|
|
358
371
|
|
|
359
372
|
# Prepare JSF file for PINGMapper
|
|
@@ -374,6 +387,7 @@ def read_master_func(logfilename='',
|
|
|
374
387
|
sys.exit()
|
|
375
388
|
|
|
376
389
|
nav_available = bool(getattr(sonar_obj, 'has_position', True))
|
|
390
|
+
side_scan_only = bool(side_scan_only)
|
|
377
391
|
|
|
378
392
|
# Sonar-only fallback for sources without navigation fields (e.g., some Cerulean logs).
|
|
379
393
|
# Disable filters that rely on geospatial motion/position so processing can continue.
|
|
@@ -546,6 +560,19 @@ def read_master_func(logfilename='',
|
|
|
546
560
|
else:
|
|
547
561
|
pass
|
|
548
562
|
|
|
563
|
+
if side_scan_only:
|
|
564
|
+
if len(ss_chan_avail) == 0:
|
|
565
|
+
print('\n\nSide-scan only mode enabled, but no side-scan channels were recognized. Skipping processing.')
|
|
566
|
+
if return_context:
|
|
567
|
+
return {
|
|
568
|
+
'has_sidescan': False,
|
|
569
|
+
'has_nav': nav_available,
|
|
570
|
+
}
|
|
571
|
+
return False
|
|
572
|
+
|
|
573
|
+
print('\n\nSide-scan only mode enabled. Skipping non-side-scan channels.')
|
|
574
|
+
sonObjs = [son for son in sonObjs if _is_sidescan_beam(getattr(son, 'beamName', ''))]
|
|
575
|
+
|
|
549
576
|
print(sonObjs)
|
|
550
577
|
####
|
|
551
578
|
# OLD
|
|
@@ -948,10 +975,15 @@ def read_master_func(logfilename='',
|
|
|
948
975
|
valid=True
|
|
949
976
|
elif (attMax != 0) or ("unknown" in att) or (att =="beam"):
|
|
950
977
|
valid=True
|
|
951
|
-
elif
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
978
|
+
elif att == "inst_dep_m":
|
|
979
|
+
depth_values = pd.to_numeric(df[att], errors='coerce').to_numpy(dtype=float, copy=False)
|
|
980
|
+
has_valid_instrument_depth = np.isfinite(depth_values).any() and np.nanmax(depth_values) > 0
|
|
981
|
+
if not has_valid_instrument_depth: # Automatically detect depth if instrument depth is missing/empty
|
|
982
|
+
valid=False
|
|
983
|
+
invalid[son.beam+"."+att] = False
|
|
984
|
+
detectDep = 1 if DEPTH_DETECTION_AVAILABLE else 2
|
|
985
|
+
else:
|
|
986
|
+
valid=True
|
|
955
987
|
else:
|
|
956
988
|
valid=False
|
|
957
989
|
invalid[son.beam+"."+att] = False
|
|
@@ -1096,17 +1128,23 @@ def read_master_func(logfilename='',
|
|
|
1096
1128
|
|
|
1097
1129
|
start_time = time.time()
|
|
1098
1130
|
|
|
1099
|
-
# Determine which sonObj
|
|
1100
|
-
|
|
1131
|
+
# Determine which sonObj pairs should be depth processed together.
|
|
1132
|
+
sidescan_groups = {}
|
|
1101
1133
|
for son in sonObjs:
|
|
1102
|
-
beam = son.beamName
|
|
1103
|
-
if _is_sidescan_beam(beam):
|
|
1104
|
-
|
|
1134
|
+
beam = str(son.beamName)
|
|
1135
|
+
if not _is_sidescan_beam(beam):
|
|
1136
|
+
continue
|
|
1137
|
+
|
|
1138
|
+
group_key = _sidescan_group_key(beam)
|
|
1139
|
+
group = sidescan_groups.setdefault(group_key, {})
|
|
1140
|
+
if beam.startswith('ss_port'):
|
|
1141
|
+
group['port'] = son
|
|
1142
|
+
elif beam.startswith('ss_star'):
|
|
1143
|
+
group['star'] = son
|
|
1105
1144
|
|
|
1106
|
-
|
|
1107
|
-
chunks = np.array([], dtype=int)
|
|
1145
|
+
ps_depth_jobs = []
|
|
1108
1146
|
|
|
1109
|
-
if len(
|
|
1147
|
+
if len(sidescan_groups) == 0:
|
|
1110
1148
|
print(
|
|
1111
1149
|
'\n\nNo recognized side-scan channels available for depth processing. '\
|
|
1112
1150
|
'Continuing with down-looking beams only.'
|
|
@@ -1116,20 +1154,28 @@ def read_master_func(logfilename='',
|
|
|
1116
1154
|
pltBedPick = False
|
|
1117
1155
|
remShadow = 0
|
|
1118
1156
|
else:
|
|
1119
|
-
|
|
1120
|
-
|
|
1157
|
+
for group_key, group in sorted(sidescan_groups.items()):
|
|
1158
|
+
if 'port' not in group or 'star' not in group:
|
|
1159
|
+
print(
|
|
1160
|
+
'\nSkipping side-scan depth group {} because a matching port/star pair was not found.'.format(group_key)
|
|
1161
|
+
)
|
|
1162
|
+
continue
|
|
1163
|
+
|
|
1164
|
+
psObj = portstarObj([group['port'], group['star']])
|
|
1121
1165
|
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1166
|
+
chunks = []
|
|
1167
|
+
for son in [group['port'], group['star']]:
|
|
1168
|
+
c = son._getChunkID()
|
|
1169
|
+
chunks.extend(c)
|
|
1170
|
+
del c
|
|
1126
1171
|
|
|
1127
|
-
chunks.
|
|
1128
|
-
|
|
1172
|
+
chunks = np.unique(chunks).astype(int)
|
|
1173
|
+
if len(chunks) == 0:
|
|
1174
|
+
continue
|
|
1129
1175
|
|
|
1130
|
-
|
|
1176
|
+
ps_depth_jobs.append((group_key, psObj, chunks))
|
|
1131
1177
|
|
|
1132
|
-
if len(
|
|
1178
|
+
if len(ps_depth_jobs) == 0:
|
|
1133
1179
|
print(
|
|
1134
1180
|
'\n\nNo valid side-scan chunks available for depth processing. '\
|
|
1135
1181
|
'Continuing with down-looking beams only.'
|
|
@@ -1142,7 +1188,12 @@ def read_master_func(logfilename='',
|
|
|
1142
1188
|
# # Automatically estimate depth
|
|
1143
1189
|
if detectDep > 0:
|
|
1144
1190
|
# Check if depth detection dependencies are available
|
|
1145
|
-
if not DEPTH_DETECTION_AVAILABLE:
|
|
1191
|
+
if detectDep == 1 and not DEPTH_DETECTION_AVAILABLE:
|
|
1192
|
+
print('\n\nML depth detection dependencies are unavailable.')
|
|
1193
|
+
print('Falling back to binary-threshold depth detection...\n')
|
|
1194
|
+
detectDep = 2
|
|
1195
|
+
|
|
1196
|
+
if detectDep == 1 and not DEPTH_DETECTION_AVAILABLE:
|
|
1146
1197
|
print('\n\nCannot estimate depth automatically:')
|
|
1147
1198
|
print('TensorFlow, Transformers, and/or Doodleverse Utils are not installed.')
|
|
1148
1199
|
print('These packages are required for automatic depth detection.')
|
|
@@ -1152,37 +1203,28 @@ def read_master_func(logfilename='',
|
|
|
1152
1203
|
autoBed = False
|
|
1153
1204
|
saveDepth = True
|
|
1154
1205
|
else:
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
psObj
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
# Parallel estimate depth for each chunk using appropriate method
|
|
1178
|
-
r = Parallel(n_jobs=safe_n_jobs(len(chunks), threadCnt))(delayed(psObj._detectDepth)(detectDep, int(chunk), USE_GPU, tileFile) for chunk in tqdm(chunks))
|
|
1179
|
-
|
|
1180
|
-
# store the depth predictions in the class
|
|
1181
|
-
for ret in r:
|
|
1182
|
-
psObj.portDepDetect[ret[2]] = ret[0]
|
|
1183
|
-
psObj.starDepDetect[ret[2]] = ret[1]
|
|
1184
|
-
del ret
|
|
1185
|
-
del r
|
|
1206
|
+
total_chunks = sum(len(chunks) for _, _, chunks in ps_depth_jobs)
|
|
1207
|
+
print('\n\nAutomatically estimating depth for', total_chunks, 'chunks across', len(ps_depth_jobs), 'side-scan group(s):')
|
|
1208
|
+
|
|
1209
|
+
for group_key, psObj, chunks in ps_depth_jobs:
|
|
1210
|
+
psObj.portDepDetect = {}
|
|
1211
|
+
psObj.starDepDetect = {}
|
|
1212
|
+
|
|
1213
|
+
if detectDep == 1:
|
|
1214
|
+
depthModelVer = 'Bedpick_Zheng2021_Segmentation_unet_v1.0'
|
|
1215
|
+
psObj.configfile = os.path.join(modelDir, depthModelVer, 'config', depthModelVer+'.json')
|
|
1216
|
+
psObj.weights = os.path.join(modelDir, depthModelVer, 'weights', depthModelVer+'_fullmodel.h5')
|
|
1217
|
+
print('\n\tGroup {}: Using Zheng et al. 2021 method. Loading model: {}'.format(group_key, os.path.basename(psObj.weights)))
|
|
1218
|
+
elif detectDep == 2:
|
|
1219
|
+
print('\n\tGroup {}: Using binary thresholding...'.format(group_key))
|
|
1220
|
+
|
|
1221
|
+
r = Parallel(n_jobs=safe_n_jobs(len(chunks), threadCnt))(delayed(psObj._detectDepth)(detectDep, int(chunk), USE_GPU, tileFile) for chunk in tqdm(chunks))
|
|
1222
|
+
|
|
1223
|
+
for ret in r:
|
|
1224
|
+
psObj.portDepDetect[ret[2]] = ret[0]
|
|
1225
|
+
psObj.starDepDetect[ret[2]] = ret[1]
|
|
1226
|
+
del ret
|
|
1227
|
+
del r
|
|
1186
1228
|
|
|
1187
1229
|
# Flag indicating depth autmatically estimated
|
|
1188
1230
|
autoBed = True
|
|
@@ -1200,9 +1242,10 @@ def read_master_func(logfilename='',
|
|
|
1200
1242
|
|
|
1201
1243
|
if saveDepth:
|
|
1202
1244
|
|
|
1203
|
-
if ss_chan_avail and
|
|
1204
|
-
|
|
1205
|
-
|
|
1245
|
+
if ss_chan_avail and len(ps_depth_jobs) > 0:
|
|
1246
|
+
depDF = []
|
|
1247
|
+
for _, psObj, chunks in ps_depth_jobs:
|
|
1248
|
+
depDF.append(psObj._saveDepth(chunks, detectDep, smthDep, adjDep, instDepAvail))
|
|
1206
1249
|
else:
|
|
1207
1250
|
depDF = []
|
|
1208
1251
|
|
|
@@ -1259,7 +1302,7 @@ def read_master_func(logfilename='',
|
|
|
1259
1302
|
del depDF
|
|
1260
1303
|
|
|
1261
1304
|
# Cleanup
|
|
1262
|
-
|
|
1305
|
+
for _, psObj, _ in ps_depth_jobs:
|
|
1263
1306
|
psObj._cleanup()
|
|
1264
1307
|
|
|
1265
1308
|
print("\nDone!")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '5.3.6'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pingmapper
|
|
3
|
-
Version: 5.3.
|
|
3
|
+
Version: 5.3.6
|
|
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.4'
|
|
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.3.4 → pingmapper-5.3.6}/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.4 → pingmapper-5.3.6}/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.4 → pingmapper-5.3.6}/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
|