pingmapper 5.5.2__tar.gz → 5.5.4__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.5.2 → pingmapper-5.5.4}/PKG-INFO +1 -1
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/class_rectObj.py +74 -8
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/class_sonObj.py +104 -18
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/default_params.json +1 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/funcs_rectify.py +34 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/gui_main.py +8 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/main_readFiles.py +7 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/main_rectify.py +16 -0
- pingmapper-5.5.4/pingmapper/version.py +1 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper.egg-info/PKG-INFO +1 -1
- pingmapper-5.5.2/pingmapper/version.py +0 -1
- {pingmapper-5.5.2 → pingmapper-5.5.4}/LICENSE +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/README.md +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/__init__.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/__main__.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/class_mapSubstrateObj.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/class_portstarObj.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/class_sonObj_nadirgaptest.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/doWork.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/funcs_common.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/funcs_model.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/main_mapSubstrate.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/nonGUI_batch_main.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/nonGui_main.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/processing_scripts/main_batchDirectory_2024-01-18_0926.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/processing_scripts/main_batchDirectory_2024-01-18_0929.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/scratch/funcs_pyhum_correct.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/scratch/main.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/scratch/main_batchDirectory.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/self_check.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/test_PINGMapper.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/test_cli_self_check.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/test_dq_filter.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/test_time.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/DRAFT_Workflows/avg_predictions_Mussel_WBL.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/DRAFT_Workflows/gen_centerline.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/DRAFT_Workflows/gen_centerline_from_bankline.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/DRAFT_Workflows/gen_centerline_trkpnts_fitspline_DRAFT.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/DRAFT_Workflows/testEXAMPLE_mosaic_logit.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/RawEGN_avg_predictions.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/Substrate_Summaries/00_substrate_logits_mosaic_transects.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/Substrate_Summaries/00_substrate_shps_mosaic_transects.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/Substrate_Summaries/01_gen_centerline_from_coverage.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/Substrate_Summaries/02_gen_summary_stamp_shps.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/Substrate_Summaries/03_gen_summary_shp.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/Substrate_Summaries/04_combine_summary_shp_csv.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/Substrate_Summaries/05_gen_summary_shp_plots.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/Substrate_Summaries/06_compare_raw-egn_volume.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/Substrate_Summaries/08_raw-egn_hardReacheFreq_hist.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/Substrate_Summaries/09_raw-egn_PatchSize_density.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/Substrate_Summaries/summarize_project_substrate.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/export_coverage.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper/utils/main_mosaic_transects.py +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper.egg-info/SOURCES.txt +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper.egg-info/dependency_links.txt +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper.egg-info/requires.txt +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pingmapper.egg-info/top_level.txt +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/pyproject.toml +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/setup.cfg +0 -0
- {pingmapper-5.5.2 → pingmapper-5.5.4}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pingmapper
|
|
3
|
-
Version: 5.5.
|
|
3
|
+
Version: 5.5.4
|
|
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
|
|
@@ -554,18 +554,22 @@ class rectObj(sonObj):
|
|
|
554
554
|
fb[lats] = fb[ylat]
|
|
555
555
|
|
|
556
556
|
if es not in fb.columns:
|
|
557
|
-
if
|
|
558
|
-
|
|
559
|
-
|
|
557
|
+
if lons in fb.columns and lats in fb.columns:
|
|
558
|
+
# Prefer projecting lon/lat through self.trans (the correct
|
|
559
|
+
# target EPSG) rather than reusing a raw xutm column, which
|
|
560
|
+
# may hold a different (e.g. Humminbird-internal Web Mercator)
|
|
561
|
+
# coordinate system and silently corrupt downstream geometry.
|
|
560
562
|
e_fb, _ = self.trans(fb[lons].to_numpy(), fb[lats].to_numpy())
|
|
561
563
|
fb[es] = e_fb
|
|
564
|
+
elif xutm in fb.columns:
|
|
565
|
+
fb[es] = fb[xutm]
|
|
562
566
|
|
|
563
567
|
if ns not in fb.columns:
|
|
564
|
-
if
|
|
565
|
-
fb[ns] = fb[yutm]
|
|
566
|
-
elif lons in fb.columns and lats in fb.columns:
|
|
568
|
+
if lons in fb.columns and lats in fb.columns:
|
|
567
569
|
_, n_fb = self.trans(fb[lons].to_numpy(), fb[lats].to_numpy())
|
|
568
570
|
fb[ns] = n_fb
|
|
571
|
+
elif yutm in fb.columns:
|
|
572
|
+
fb[ns] = fb[yutm]
|
|
569
573
|
|
|
570
574
|
if 'cog' not in fb.columns:
|
|
571
575
|
if 'instr_heading' in fb.columns:
|
|
@@ -1099,6 +1103,26 @@ class rectObj(sonObj):
|
|
|
1099
1103
|
else:
|
|
1100
1104
|
continue
|
|
1101
1105
|
|
|
1106
|
+
# Guard against spline overshoot: the linear spline fit to range
|
|
1107
|
+
# extent coordinates can extrapolate far beyond the sonar's actual
|
|
1108
|
+
# range near gaps left by upstream filtering (time/speed/coord
|
|
1109
|
+
# outlier filters). Fall back to the geometrically-derived
|
|
1110
|
+
# (un-smoothed) range extent point for any ping whose smoothed
|
|
1111
|
+
# point lands unreasonably far from its trackline origin.
|
|
1112
|
+
if {'record_num', 'range_lon', 'range_lat', 'utm_es', 'utm_ns', 'range'}.issubset(schunkDF.columns):
|
|
1113
|
+
e_smth_chk, n_smth_chk = self.trans(smthChunk['range_lons'].to_numpy(), smthChunk['range_lats'].to_numpy())
|
|
1114
|
+
chk = schunkDF[['record_num', 'utm_es', 'utm_ns', 'range', 'range_lon', 'range_lat']].set_index('record_num')
|
|
1115
|
+
chk = chk.reindex(smthChunk['record_num'].to_numpy())
|
|
1116
|
+
|
|
1117
|
+
dist = np.hypot(e_smth_chk - chk['utm_es'].to_numpy(), n_smth_chk - chk['utm_ns'].to_numpy())
|
|
1118
|
+
max_expected = np.maximum(chk['range'].to_numpy(), 1.0) * 3.0 + 50.0
|
|
1119
|
+
bad = np.isfinite(dist) & (dist > max_expected)
|
|
1120
|
+
n_bad = int(np.sum(bad))
|
|
1121
|
+
if n_bad > 0:
|
|
1122
|
+
print(f"\n _interpRangeCoords: corrected {n_bad} spline-overshoot range extent point(s) in chunk {chunk}.")
|
|
1123
|
+
smthChunk.loc[bad, 'range_lons'] = chk['range_lon'].to_numpy()[bad]
|
|
1124
|
+
smthChunk.loc[bad, 'range_lats'] = chk['range_lat'].to_numpy()[bad]
|
|
1125
|
+
|
|
1102
1126
|
rs_chunks.append(smthChunk.copy())
|
|
1103
1127
|
|
|
1104
1128
|
##################################################
|
|
@@ -2724,6 +2748,8 @@ class rectObj(sonObj):
|
|
|
2724
2748
|
img = self.sonDat.copy()
|
|
2725
2749
|
if son:
|
|
2726
2750
|
img = self._reserve_zero_for_nodata(img, preserve_zeros=True)
|
|
2751
|
+
if bool(getattr(self, 'sonar_db_transform', False)) or bool(getattr(self, 'sonar_clahe', False)):
|
|
2752
|
+
img = self._apply_display_enhancements(img)
|
|
2727
2753
|
apply_post_rect_colormap = use_16bit and self._rect_colormap_selected(son=son)
|
|
2728
2754
|
source_scale_bounds = None
|
|
2729
2755
|
if apply_post_rect_colormap:
|
|
@@ -2747,6 +2773,8 @@ class rectObj(sonObj):
|
|
|
2747
2773
|
out = np.flip(np.flip(np.flip(out,1),0),1)
|
|
2748
2774
|
if use_16bit:
|
|
2749
2775
|
out = out.astype(np.float32)
|
|
2776
|
+
elif bool(getattr(self, 'sonar_db_transform', False)) or bool(getattr(self, 'sonar_clahe', False)):
|
|
2777
|
+
out = out.astype(np.float32)
|
|
2750
2778
|
else:
|
|
2751
2779
|
out = out.astype('uint8')
|
|
2752
2780
|
|
|
@@ -2780,7 +2808,10 @@ class rectObj(sonObj):
|
|
|
2780
2808
|
out16 = out16_raw
|
|
2781
2809
|
self._write_rect_geotiff(gtiff, out16, epsg, transform, colormap=None)
|
|
2782
2810
|
else:
|
|
2783
|
-
|
|
2811
|
+
if bool(getattr(self, 'sonar_db_transform', False)) or bool(getattr(self, 'sonar_clahe', False)):
|
|
2812
|
+
out8 = self._convert_son_dat_to_uint8(out)
|
|
2813
|
+
else:
|
|
2814
|
+
out8 = np.clip(out, 0, 255).astype(np.uint8)
|
|
2784
2815
|
self._write_rect_geotiff(gtiff, out8, epsg, transform, colormap=self.son_colorMap)
|
|
2785
2816
|
|
|
2786
2817
|
del out, img
|
|
@@ -2815,6 +2846,8 @@ class rectObj(sonObj):
|
|
|
2815
2846
|
img = self.sonDat
|
|
2816
2847
|
if son:
|
|
2817
2848
|
img = self._reserve_zero_for_nodata(img, preserve_zeros=True)
|
|
2849
|
+
if bool(getattr(self, 'sonar_db_transform', False)) or bool(getattr(self, 'sonar_clahe', False)):
|
|
2850
|
+
img = self._apply_display_enhancements(img)
|
|
2818
2851
|
apply_post_rect_colormap = son and use_16bit and self._rect_colormap_selected(son=son)
|
|
2819
2852
|
source_scale_bounds = None
|
|
2820
2853
|
if apply_post_rect_colormap:
|
|
@@ -2887,6 +2920,8 @@ class rectObj(sonObj):
|
|
|
2887
2920
|
out = np.flip(np.flip(np.flip(out,1),0),1)
|
|
2888
2921
|
if son and use_16bit:
|
|
2889
2922
|
out = out.astype(np.float32)
|
|
2923
|
+
elif son and (bool(getattr(self, 'sonar_db_transform', False)) or bool(getattr(self, 'sonar_clahe', False))):
|
|
2924
|
+
out = out.astype(np.float32)
|
|
2890
2925
|
else:
|
|
2891
2926
|
out = out.astype('uint8')
|
|
2892
2927
|
|
|
@@ -2926,7 +2961,10 @@ class rectObj(sonObj):
|
|
|
2926
2961
|
self._write_rect_geotiff(gtiff, out16, epsg, transform, colormap=None)
|
|
2927
2962
|
else:
|
|
2928
2963
|
colormap = self.son_colorMap if son else class_colormap
|
|
2929
|
-
|
|
2964
|
+
if son and (bool(getattr(self, 'sonar_db_transform', False)) or bool(getattr(self, 'sonar_clahe', False))):
|
|
2965
|
+
out8 = self._convert_son_dat_to_uint8(out)
|
|
2966
|
+
else:
|
|
2967
|
+
out8 = np.clip(out, 0, 255).astype(np.uint8)
|
|
2930
2968
|
self._write_rect_geotiff(gtiff, out8, epsg, transform, colormap=colormap)
|
|
2931
2969
|
|
|
2932
2970
|
del out
|
|
@@ -2986,6 +3024,34 @@ class rectObj(sonObj):
|
|
|
2986
3024
|
# Set output name
|
|
2987
3025
|
f_out = f.replace('temp.tif', '.tif')
|
|
2988
3026
|
|
|
3027
|
+
# Sanity check the georeferenced extent before warping. A corrupt/outlier
|
|
3028
|
+
# coordinate upstream can produce a geotransform spanning thousands of km,
|
|
3029
|
+
# which causes gdal.Warp to attempt allocating a raster with an absurd
|
|
3030
|
+
# pixel count (surfacing as a misleading "free disk space" GDAL error).
|
|
3031
|
+
src = gdal.Open(f)
|
|
3032
|
+
if src is not None:
|
|
3033
|
+
gt = src.GetGeoTransform()
|
|
3034
|
+
width_m = abs(gt[1]) * src.RasterXSize
|
|
3035
|
+
height_m = abs(gt[5]) * src.RasterYSize
|
|
3036
|
+
src = None
|
|
3037
|
+
|
|
3038
|
+
est_cols = width_m / pix_res
|
|
3039
|
+
est_rows = height_m / pix_res
|
|
3040
|
+
|
|
3041
|
+
_MAX_PIXELS = 5e8 # ~500 million pixels safety cap
|
|
3042
|
+
if est_cols * est_rows > _MAX_PIXELS:
|
|
3043
|
+
print(
|
|
3044
|
+
f"\nWARNING: Skipping pixel resolution resize for {os.path.basename(f)}. "
|
|
3045
|
+
f"Georeferenced extent ({width_m:.1f} x {height_m:.1f} m) at {pix_res} m resolution "
|
|
3046
|
+
f"would require ~{est_cols*est_rows:,.0f} pixels. This indicates a corrupt or outlier "
|
|
3047
|
+
f"ping coordinate in this chunk. Keeping the un-resized GeoTiff instead."
|
|
3048
|
+
)
|
|
3049
|
+
try:
|
|
3050
|
+
os.rename(f, f_out)
|
|
3051
|
+
except OSError:
|
|
3052
|
+
pass
|
|
3053
|
+
return
|
|
3054
|
+
|
|
2989
3055
|
# Reopen f and warp to pix_res
|
|
2990
3056
|
t = gdal.Warp(f_out, f, xRes = pix_res, yRes = pix_res, targetAlignedPixels=True)
|
|
2991
3057
|
|
|
@@ -1319,6 +1319,44 @@ class sonObj(object):
|
|
|
1319
1319
|
if not use_db and not use_clahe:
|
|
1320
1320
|
return sonDat
|
|
1321
1321
|
|
|
1322
|
+
arr = self._apply_display_db_transform(sonDat)
|
|
1323
|
+
if arr.size == 0:
|
|
1324
|
+
return arr
|
|
1325
|
+
|
|
1326
|
+
valid = arr > 0
|
|
1327
|
+
if not np.any(valid):
|
|
1328
|
+
return arr
|
|
1329
|
+
|
|
1330
|
+
if use_clahe:
|
|
1331
|
+
clip_limit = float(getattr(self, 'sonar_clahe_clip_limit', 0.01))
|
|
1332
|
+
if (not np.isfinite(clip_limit)) or clip_limit <= 0:
|
|
1333
|
+
clip_limit = 0.01
|
|
1334
|
+
|
|
1335
|
+
global_bounds = None
|
|
1336
|
+
if bool(getattr(self, 'sonar_clahe_global', True)):
|
|
1337
|
+
global_bounds = self._ensure_clahe_global_bounds()
|
|
1338
|
+
|
|
1339
|
+
if global_bounds is not None:
|
|
1340
|
+
arr01 = self._normalize_with_bounds(arr, global_bounds[0], global_bounds[1])
|
|
1341
|
+
else:
|
|
1342
|
+
max_val = float(np.nanmax(arr))
|
|
1343
|
+
if not (np.isfinite(max_val) and max_val > 0):
|
|
1344
|
+
return arr
|
|
1345
|
+
arr01 = np.clip(arr / max_val, 0.0, 1.0)
|
|
1346
|
+
|
|
1347
|
+
try:
|
|
1348
|
+
arr01 = exposure.equalize_adapthist(arr01, clip_limit=clip_limit)
|
|
1349
|
+
arr = np.asarray(arr01, dtype=np.float32) * 255.0
|
|
1350
|
+
except MemoryError:
|
|
1351
|
+
arr = self._apply_clahe_chunked(arr01, clip_limit)
|
|
1352
|
+
except np.core._exceptions._ArrayMemoryError:
|
|
1353
|
+
arr = self._apply_clahe_chunked(arr01, clip_limit)
|
|
1354
|
+
|
|
1355
|
+
# Preserve no-data convention for empty samples.
|
|
1356
|
+
arr[~valid] = 0.0
|
|
1357
|
+
return arr
|
|
1358
|
+
|
|
1359
|
+
def _apply_display_db_transform(self, sonDat):
|
|
1322
1360
|
arr = np.asarray(sonDat, dtype=np.float32)
|
|
1323
1361
|
if arr.size == 0:
|
|
1324
1362
|
return arr
|
|
@@ -1330,7 +1368,7 @@ class sonObj(object):
|
|
|
1330
1368
|
if not np.any(valid):
|
|
1331
1369
|
return arr
|
|
1332
1370
|
|
|
1333
|
-
if
|
|
1371
|
+
if bool(getattr(self, 'sonar_db_transform', False)):
|
|
1334
1372
|
# Use the smallest positive intensity as the log floor to avoid
|
|
1335
1373
|
# collapsing zeros to -inf and to preserve relative low amplitudes.
|
|
1336
1374
|
floor = float(np.nanmin(arr[valid]))
|
|
@@ -1340,25 +1378,73 @@ class sonObj(object):
|
|
|
1340
1378
|
arr = 20.0 * np.log10(arr)
|
|
1341
1379
|
arr = arr - np.nanmin(arr)
|
|
1342
1380
|
|
|
1343
|
-
if use_clahe:
|
|
1344
|
-
max_val = float(np.nanmax(arr))
|
|
1345
|
-
if np.isfinite(max_val) and max_val > 0:
|
|
1346
|
-
arr01 = np.clip(arr / max_val, 0.0, 1.0)
|
|
1347
|
-
clip_limit = float(getattr(self, 'sonar_clahe_clip_limit', 0.01))
|
|
1348
|
-
if (not np.isfinite(clip_limit)) or clip_limit <= 0:
|
|
1349
|
-
clip_limit = 0.01
|
|
1350
|
-
try:
|
|
1351
|
-
arr01 = exposure.equalize_adapthist(arr01, clip_limit=clip_limit)
|
|
1352
|
-
arr = np.asarray(arr01, dtype=np.float32)
|
|
1353
|
-
except MemoryError:
|
|
1354
|
-
arr = self._apply_clahe_chunked(arr01, clip_limit)
|
|
1355
|
-
except np.core._exceptions._ArrayMemoryError:
|
|
1356
|
-
arr = self._apply_clahe_chunked(arr01, clip_limit)
|
|
1357
|
-
|
|
1358
|
-
# Preserve no-data convention for empty samples.
|
|
1359
1381
|
arr[~valid] = 0.0
|
|
1360
1382
|
return arr
|
|
1361
1383
|
|
|
1384
|
+
def _ensure_clahe_global_bounds(self):
|
|
1385
|
+
if not bool(getattr(self, 'sonar_clahe_global', True)):
|
|
1386
|
+
return None
|
|
1387
|
+
|
|
1388
|
+
cache = getattr(self, '_sonar_clahe_global_bounds', None)
|
|
1389
|
+
if isinstance(cache, tuple) and len(cache) == 2:
|
|
1390
|
+
lo, hi = cache
|
|
1391
|
+
if np.isfinite(lo) and np.isfinite(hi) and hi > lo:
|
|
1392
|
+
return cache
|
|
1393
|
+
|
|
1394
|
+
if not hasattr(self, 'sonMetaDF'):
|
|
1395
|
+
self._loadSonMeta()
|
|
1396
|
+
|
|
1397
|
+
if not hasattr(self, 'sonMetaDF') or self.sonMetaDF is None or len(self.sonMetaDF) == 0:
|
|
1398
|
+
return None
|
|
1399
|
+
|
|
1400
|
+
saved_sonDat = getattr(self, 'sonDat', None)
|
|
1401
|
+
saved_sonDat16 = getattr(self, 'sonDat16', None)
|
|
1402
|
+
saved_pingMax = getattr(self, 'pingMax', None)
|
|
1403
|
+
|
|
1404
|
+
chunk_ids = pd.unique(self.sonMetaDF['chunk_id'])
|
|
1405
|
+
sample_cap = int(getattr(self, 'sonar_clahe_global_sample_cap', 200000))
|
|
1406
|
+
if sample_cap <= 0:
|
|
1407
|
+
sample_cap = 200000
|
|
1408
|
+
|
|
1409
|
+
sampled_vals = []
|
|
1410
|
+
for chunk in chunk_ids:
|
|
1411
|
+
self._getScanChunkSingle(int(chunk))
|
|
1412
|
+
arr = self._apply_display_db_transform(self.sonDat)
|
|
1413
|
+
valid = arr > 0
|
|
1414
|
+
if not np.any(valid):
|
|
1415
|
+
continue
|
|
1416
|
+
|
|
1417
|
+
vals = arr[valid].astype(np.float32, copy=False)
|
|
1418
|
+
if vals.size > sample_cap:
|
|
1419
|
+
idx = np.linspace(0, vals.size - 1, sample_cap, dtype=np.int64)
|
|
1420
|
+
vals = vals[idx]
|
|
1421
|
+
sampled_vals.append(vals)
|
|
1422
|
+
|
|
1423
|
+
if len(sampled_vals) == 0:
|
|
1424
|
+
self.sonDat = saved_sonDat
|
|
1425
|
+
self.sonDat16 = saved_sonDat16
|
|
1426
|
+
self.pingMax = saved_pingMax
|
|
1427
|
+
return None
|
|
1428
|
+
|
|
1429
|
+
pooled = np.concatenate(sampled_vals)
|
|
1430
|
+
lo = float(np.nanpercentile(pooled, 1.0))
|
|
1431
|
+
hi = float(np.nanpercentile(pooled, 99.5))
|
|
1432
|
+
if (not np.isfinite(lo)) or (not np.isfinite(hi)) or hi <= lo:
|
|
1433
|
+
lo = float(np.nanmin(pooled))
|
|
1434
|
+
hi = float(np.nanmax(pooled))
|
|
1435
|
+
|
|
1436
|
+
if (not np.isfinite(lo)) or (not np.isfinite(hi)) or hi <= lo:
|
|
1437
|
+
self.sonDat = saved_sonDat
|
|
1438
|
+
self.sonDat16 = saved_sonDat16
|
|
1439
|
+
self.pingMax = saved_pingMax
|
|
1440
|
+
return None
|
|
1441
|
+
|
|
1442
|
+
self._sonar_clahe_global_bounds = (lo, hi)
|
|
1443
|
+
self.sonDat = saved_sonDat
|
|
1444
|
+
self.sonDat16 = saved_sonDat16
|
|
1445
|
+
self.pingMax = saved_pingMax
|
|
1446
|
+
return self._sonar_clahe_global_bounds
|
|
1447
|
+
|
|
1362
1448
|
def _apply_clahe_chunked(self, arr01, clip_limit):
|
|
1363
1449
|
"""Apply CLAHE in row-wise stripes to reduce peak memory usage."""
|
|
1364
1450
|
src = np.asarray(arr01, dtype=np.float32)
|
|
@@ -1378,7 +1464,7 @@ class sonObj(object):
|
|
|
1378
1464
|
end = min(start + stripe_rows, h)
|
|
1379
1465
|
stripe = src[start:end, :]
|
|
1380
1466
|
stripe_eq = exposure.equalize_adapthist(stripe, clip_limit=clip_limit)
|
|
1381
|
-
out[start:end, :] = np.asarray(stripe_eq, dtype=np.float32)
|
|
1467
|
+
out[start:end, :] = np.asarray(stripe_eq, dtype=np.float32) * 255.0
|
|
1382
1468
|
|
|
1383
1469
|
return out
|
|
1384
1470
|
|
|
@@ -149,11 +149,44 @@ def smoothTrackline(projDir='', x_offset='', y_offset='', nchunk ='', cog=True,
|
|
|
149
149
|
# filter = int(nchunk*0.1)
|
|
150
150
|
|
|
151
151
|
# print('\n\n\ntransect:', name, 'filter:', filter)
|
|
152
|
+
# Snapshot raw coordinates before _interpTrack() may drop duplicates
|
|
153
|
+
# in-place, so we can validate/repair the smoothed output below.
|
|
154
|
+
raw_lon = group['lon'].copy()
|
|
155
|
+
raw_lat = group['lat'].copy()
|
|
156
|
+
|
|
152
157
|
smoothed = son._interpTrack(df=group, dropDup=True, filt=filter, deg=3)
|
|
153
158
|
|
|
154
159
|
# smooth trackline fit
|
|
155
160
|
# if smoothed is not None:
|
|
156
161
|
if len(smoothed.columns) > 4:
|
|
162
|
+
# Guard against cubic spline overshoot (Runge's phenomenon) across
|
|
163
|
+
# long or gappy transects (e.g. gaps left by time/speed/coord
|
|
164
|
+
# outlier filters). Fall back to the raw GPS fix for any ping
|
|
165
|
+
# whose smoothed position was displaced further than the
|
|
166
|
+
# transect's own spatial extent would allow.
|
|
167
|
+
raw_e, raw_n = son.trans(raw_lon.to_numpy(), raw_lat.to_numpy())
|
|
168
|
+
raw_e = pd.Series(raw_e, index=raw_lon.index).reindex(smoothed.index).to_numpy()
|
|
169
|
+
raw_n = pd.Series(raw_n, index=raw_lat.index).reindex(smoothed.index).to_numpy()
|
|
170
|
+
|
|
171
|
+
smth_e, smth_n = son.trans(smoothed['lons'].to_numpy(), smoothed['lats'].to_numpy())
|
|
172
|
+
|
|
173
|
+
dist = np.hypot(smth_e - raw_e, smth_n - raw_n)
|
|
174
|
+
span = np.hypot(np.nanmax(raw_e) - np.nanmin(raw_e), np.nanmax(raw_n) - np.nanmin(raw_n))
|
|
175
|
+
max_allowed = max(span, 50.0) * 2.0
|
|
176
|
+
|
|
177
|
+
bad = np.isfinite(dist) & (dist > max_allowed)
|
|
178
|
+
n_bad = int(np.sum(bad))
|
|
179
|
+
if n_bad > 0:
|
|
180
|
+
print(f"\n smoothTrackline: corrected {n_bad} spline-overshoot trackline point(s) in transect {name}.")
|
|
181
|
+
fix_lon = pd.Series(raw_lon.to_numpy(), index=raw_lon.index).reindex(smoothed.index).to_numpy()
|
|
182
|
+
fix_lat = pd.Series(raw_lat.to_numpy(), index=raw_lat.index).reindex(smoothed.index).to_numpy()
|
|
183
|
+
smoothed.loc[bad, 'lons'] = fix_lon[bad]
|
|
184
|
+
smoothed.loc[bad, 'lats'] = fix_lat[bad]
|
|
185
|
+
if 'utm_es' in smoothed.columns and 'utm_ns' in smoothed.columns:
|
|
186
|
+
e_fix, n_fix = son.trans(smoothed.loc[bad, 'lons'].to_numpy(), smoothed.loc[bad, 'lats'].to_numpy())
|
|
187
|
+
smoothed.loc[bad, 'utm_es'] = e_fix
|
|
188
|
+
smoothed.loc[bad, 'utm_ns'] = n_fix
|
|
189
|
+
|
|
157
190
|
smoothed['transect'] = int(name)
|
|
158
191
|
sDF = pd.concat([sDF, smoothed], ignore_index=False)
|
|
159
192
|
|
|
@@ -166,6 +199,7 @@ def smoothTrackline(projDir='', x_offset='', y_offset='', nchunk ='', cog=True,
|
|
|
166
199
|
|
|
167
200
|
del smoothed
|
|
168
201
|
|
|
202
|
+
|
|
169
203
|
# Save sonDF
|
|
170
204
|
if len(transect_dropped) > 0:
|
|
171
205
|
|
|
@@ -321,6 +321,12 @@ def gui(batch: bool):
|
|
|
321
321
|
default=default_params.get('sonar_clahe', False),
|
|
322
322
|
tooltip=tip_clahe,
|
|
323
323
|
)
|
|
324
|
+
check_sonar_clahe_global = sg.Checkbox(
|
|
325
|
+
'Global CLAHE reference',
|
|
326
|
+
key='sonar_clahe_global',
|
|
327
|
+
default=default_params.get('sonar_clahe_global', True),
|
|
328
|
+
tooltip='Use one project-wide intensity reference before CLAHE so chunk brightness stays consistent. This adds a prepass, but reduces chunk-to-chunk contrast shifts.',
|
|
329
|
+
)
|
|
324
330
|
text_clahe_clip = sg.Text('CLAHE Clip Limit', size=(20,1))
|
|
325
331
|
in_clahe_clip = sg.Input(
|
|
326
332
|
key='sonar_clahe_clip_limit',
|
|
@@ -335,6 +341,7 @@ def gui(batch: bool):
|
|
|
335
341
|
[text_egn_gain, slide_egn_gain],
|
|
336
342
|
[check_sonar_db],
|
|
337
343
|
[check_sonar_clahe],
|
|
344
|
+
[check_sonar_clahe_global],
|
|
338
345
|
[text_clahe_clip, in_clahe_clip],
|
|
339
346
|
],
|
|
340
347
|
pad=0,
|
|
@@ -837,6 +844,7 @@ def gui(batch: bool):
|
|
|
837
844
|
'tone_gain':float(values['tone_gain']),
|
|
838
845
|
'sonar_db_transform':values['sonar_db_transform'],
|
|
839
846
|
'sonar_clahe':values['sonar_clahe'],
|
|
847
|
+
'sonar_clahe_global':values['sonar_clahe_global'],
|
|
840
848
|
'sonar_clahe_clip_limit':float(values['sonar_clahe_clip_limit']),
|
|
841
849
|
'wcp':values['wcp'],
|
|
842
850
|
'wcm':values['wcm'],
|
|
@@ -720,6 +720,7 @@ def read_master_func(logfilename='',
|
|
|
720
720
|
tone_gain=1.0,
|
|
721
721
|
sonar_db_transform=False,
|
|
722
722
|
sonar_clahe=False,
|
|
723
|
+
sonar_clahe_global=True,
|
|
723
724
|
sonar_clahe_clip_limit=0.01,
|
|
724
725
|
wcp=False,
|
|
725
726
|
wcm=False,
|
|
@@ -1209,7 +1210,10 @@ def read_master_func(logfilename='',
|
|
|
1209
1210
|
son.tone_gain = tone_gain
|
|
1210
1211
|
son.sonar_db_transform = bool(sonar_db_transform)
|
|
1211
1212
|
son.sonar_clahe = bool(sonar_clahe)
|
|
1213
|
+
son.sonar_clahe_global = bool(sonar_clahe_global)
|
|
1212
1214
|
son.sonar_clahe_clip_limit = float(sonar_clahe_clip_limit)
|
|
1215
|
+
if son.sonar_clahe:
|
|
1216
|
+
son._ensure_clahe_global_bounds()
|
|
1213
1217
|
# Do range crop, if necessary
|
|
1214
1218
|
if cropRange > 0.0:
|
|
1215
1219
|
if file_type == '.xtf' and _is_sidescan_beam(getattr(son, 'beamName', '')):
|
|
@@ -1405,7 +1409,10 @@ def read_master_func(logfilename='',
|
|
|
1405
1409
|
son.tone_gain = tone_gain
|
|
1406
1410
|
son.sonar_db_transform = bool(sonar_db_transform)
|
|
1407
1411
|
son.sonar_clahe = bool(sonar_clahe)
|
|
1412
|
+
son.sonar_clahe_global = bool(sonar_clahe_global)
|
|
1408
1413
|
son.sonar_clahe_clip_limit = float(sonar_clahe_clip_limit)
|
|
1414
|
+
if son.sonar_clahe:
|
|
1415
|
+
son._ensure_clahe_global_bounds()
|
|
1409
1416
|
|
|
1410
1417
|
beam = son.beamName
|
|
1411
1418
|
if wcr:
|
|
@@ -126,6 +126,10 @@ def rectify_master_func(logfilename='',
|
|
|
126
126
|
egn_stretch_factor=1,
|
|
127
127
|
tone_gamma=1.0,
|
|
128
128
|
tone_gain=1.0,
|
|
129
|
+
sonar_db_transform=False,
|
|
130
|
+
sonar_clahe=False,
|
|
131
|
+
sonar_clahe_global=True,
|
|
132
|
+
sonar_clahe_clip_limit=0.01,
|
|
129
133
|
wcp=False,
|
|
130
134
|
wcm=False,
|
|
131
135
|
wcr=False,
|
|
@@ -368,6 +372,12 @@ def rectify_master_func(logfilename='',
|
|
|
368
372
|
son.rect_wcr = rect_wcr
|
|
369
373
|
son.export_16bit = bool(export_16bit) and (not bool(getattr(son, 'son8bit', True)))
|
|
370
374
|
son.export_colormap_uint8 = bool(export_colormap_uint8)
|
|
375
|
+
son.sonar_db_transform = bool(sonar_db_transform)
|
|
376
|
+
son.sonar_clahe = bool(sonar_clahe)
|
|
377
|
+
son.sonar_clahe_global = bool(sonar_clahe_global)
|
|
378
|
+
son.sonar_clahe_clip_limit = float(sonar_clahe_clip_limit)
|
|
379
|
+
if son.sonar_clahe:
|
|
380
|
+
son._ensure_clahe_global_bounds()
|
|
371
381
|
|
|
372
382
|
# ############################################################################
|
|
373
383
|
# # Rectify Heading sonar imagery - Pingwise, not Rubbersheeting #
|
|
@@ -462,6 +472,12 @@ def rectify_master_func(logfilename='',
|
|
|
462
472
|
for son in portstar:
|
|
463
473
|
son.rect_wcp = rect_wcp
|
|
464
474
|
son.rect_wcr = rect_wcr
|
|
475
|
+
son.sonar_db_transform = bool(sonar_db_transform)
|
|
476
|
+
son.sonar_clahe = bool(sonar_clahe)
|
|
477
|
+
son.sonar_clahe_global = bool(sonar_clahe_global)
|
|
478
|
+
son.sonar_clahe_clip_limit = float(sonar_clahe_clip_limit)
|
|
479
|
+
if son.sonar_clahe:
|
|
480
|
+
son._ensure_clahe_global_bounds()
|
|
465
481
|
|
|
466
482
|
if (rect_wcp and rubberSheeting) or (rect_wcr and rubberSheeting):
|
|
467
483
|
# Always use COG for rubber sheeting
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '5.5.4'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pingmapper
|
|
3
|
-
Version: 5.5.
|
|
3
|
+
Version: 5.5.4
|
|
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.5.2'
|
|
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.5.2 → pingmapper-5.5.4}/pingmapper/utils/DRAFT_Workflows/avg_predictions_Mussel_WBL.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pingmapper-5.5.2 → pingmapper-5.5.4}/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.5.2 → pingmapper-5.5.4}/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
|