sidescanpy.analysis.shadows#

Object proposals from acoustic shadows in native slant-range data.

An object standing proud of the seabed returns a highlight on its near-range face and leaves a shadow (little or no return) behind it, further out along the same pings. detect_shadow_proposals() finds those shadows as regions well below the local seabed level, pairs each with the highlight just in front of it, and returns one proposal box per object in native (ping, sample, channel) coordinates, with features (sizes, contrast, an estimated height) to rank or filter them.

The data are averaged into cells about cell_m across track (one ping along track) before thresholding, because single samples carry several dB of speckle. A second, weaker threshold catches the short, shallow shadows of objects close to nadir, where the grazing angle is steep; those regions are kept only with a highlight in front of them. Proposals are meant to pre-fill boxes for a person to correct, so the defaults favour recall over precision.

2026 rshomberg@uri.edu

Contents

PROPOSAL_COLUMNS

Built-in mutable sequence.

ShadowProposals

Proposals found by detect_shadow_proposals().

detect_shadow_proposals

Find objects by their acoustic shadow.

sidescanpy.analysis.shadows.PROPOSAL_COLUMNS#

Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list. The argument must be an iterable if specified.

class sidescanpy.analysis.shadows.ShadowProposals(proposals=<factory>, m_per_ping=None, m_per_sample=None, cell_samples=None, params=<factory>, n_shadow_regions=0, n_rejected=<factory>, notes=<factory>)#

Bases:

Proposals found by detect_shadow_proposals().

proposals has one row per proposal (columns PROPOSAL_COLUMNS). Extents are half-open native positions along the dataset’s ping and sample axes, like NativeWindow; the box spans the highlight (when found) and the shadow. height_m is NaN where altitude is unknown. weak marks proposals from the weak-shadow pass. score is in [0, 1]: higher for a deeper shadow, a taller object (a longer shadow where altitude is unknown) and a highlight in front.

sidescanpy.analysis.shadows.detect_shadow_proposals(ds, *, value_var='backscatter_intensity', channels=(0, 1), cell_m=0.06, background_m=(3.0, 6.0), smooth_cells=(1, 3), shadow_db=6.0, highlight_db=3.0, min_shadow_m=0.2, min_pings=2, max_gap_m=0.6, highlight_search_m=2.0, min_height_m=0.0, max_height_m=3.0, max_length_m=8.0, weak_shadow_db=4.0, weak_min_shadow_m=0.1, weak_min_highlight=0.5, score_height_m=0.3, m_per_ping=None, mask_water_column=True)#

Find objects by their acoustic shadow.

For each channel:

  1. Average value_var (dB, after the tvg step) into cells of about cell_m along range, one ping each, then smooth them with a smooth_cells (pings, cells) running mean.

  2. Background: a running median over background_m (across, along track, metres).

  3. Shadow cells are at least shadow_db below the background; they are grouped into connected regions (8-connected).

  4. A region is kept if it covers at least min_pings pings and its longest run along range is at least min_shadow_m.

  5. Its highlight is the run of cells at least highlight_db above the background ending within max_gap_m before the shadow, searched up to highlight_search_m towards nadir, on each of its pings.

  6. Height from flat-seabed geometry, h = A * Ls / (R + Ls) (altitude A, slant range R to the shadow start, shadow slant length Ls), per ping, median over the region. Proposals outside [min_height_m, max_height_m] (when the height is known) or longer than max_length_m in either direction are dropped.

  7. Weak pass (unless weak_shadow_db is None): regions at least weak_shadow_db below the background that contain no cell of a proposal from steps 3-6, with a run of at least weak_min_shadow_m, go through steps 4-6 and are kept when a highlight is found on at least weak_min_highlight of their pings. They get weak=True.

score = 0.4 x shadow depth (shadow_db -> 0, 3 * shadow_db -> 1) + 0.3 x height / score_height_m (capped at 1; shadow length / 1 m where the height is unknown) + 0.3 x the fraction of pings with a highlight. Height rather than shadow length, because the shadow of the same object is much shorter near nadir.

The water column is excluded: with mask_water_column (default), samples at a slant range up to the ping’s altitude (sample * m_per_sample <= altitude_m, the same flat-seabed geometry as the height estimate) are treated as NaN, because they come before the first bottom return and their low level reads as many weak false shadows. Pings with NaN or non-positive altitude_m (or a dataset without it) are not masked; fill missing altitude first with the bottom_track correction step. ds is not modified.

NaN cells (blanked or masked water column, hull echo, masked pings) are never shadow, and a region whose near-range side lies within 0.3 m of NaN cells on most of its pings is dropped: that is the edge of the blanked area, not an object.

Parameters:
dsxr.Dataset

One frequency channel (slice pings beforehand to limit memory).

value_varstr, default “backscatter_intensity”

Values in dB.

channelstuple of int, default (0, 1)
cell_mfloat, default 0.06

Across-track cell size; rounded to a whole number of samples.

background_m(float, float), default (3.0, 6.0)

Background window, across track and along track (m).

smooth_cells(int, int), default (1, 3)

Running-mean size (pings, cells) applied before thresholding.

shadow_db, highlight_dbfloat, default 6.0, 3.0

Thresholds below / above the background (dB).

min_shadow_mfloat, default 0.2
min_pingsint, default 2
max_gap_mfloat, default 0.6
highlight_search_mfloat, default 2.0
min_height_m, max_height_mfloat, default 0.0, 3.0
max_length_mfloat, default 8.0
weak_shadow_dbfloat or None, default 4.0

Threshold of the weak pass (dB below background); None skips it.

weak_min_shadow_mfloat, default 0.1
weak_min_highlightfloat, default 0.5

Fraction of a weak region’s pings that must have a highlight.

score_height_mfloat, default 0.3

Height that earns the full height part of the score.

m_per_pingfloat, optional

Along-track spacing; default from the per-ping positions.

mask_water_columnbool, default True

Treat samples with slant range <= altitude_m as NaN (see above). False uses every sample as given.

Returns:
ShadowProposals