Skip to content

Bad-line MAD detection fixes + sub-pixel Gaussian blur with physical readout#38

Merged
jacobson30-bot merged 2 commits into
mainfrom
fix/bad-lines-and-gaussian-blur
Jun 15, 2026
Merged

Bad-line MAD detection fixes + sub-pixel Gaussian blur with physical readout#38
jacobson30-bot merged 2 commits into
mainfrom
fix/bad-lines-and-gaussian-blur

Conversation

@jacobson30-bot

Copy link
Copy Markdown
Contributor

Two independent, self-contained improvements (one commit each; reviewable separately).

1. Bad-line MAD detection — works on real low-contrast scans

Verified by the user on createc_scan_qplus_10ch_afm (Z forward). Three compounding bugs fixed in probeflow/processing/bad_lines.py:

  • Couldn't apply the correction. The adjacent-line safety limit was column-blind, so noise across many lines formed one long consecutive-line run and all detected segments were skipped. Now column-overlap-aware (_split_segments_by_adjacent_limit) — only a genuine vertical stack taller than the limit is skipped.
  • Long segments hidden/shattered. Per-row median subtraction hid defects covering >½ a row; per-pixel thresholding shattered noisy segments. Now thresholds the neighbour-referenced residual directly, bridges short noise gaps, and keeps a run only if it's a defect on its median.
  • Low-contrast lines undetectable (~1.5σ/px). Added an 11 px matched-filter window (_smooth_rows_nanaware, _MAD_SMOOTH_WINDOW_PX) before thresholding. On the sample file, threshold-5 detection went 0 → ~80 segments on exactly the bright rows, repaired with 0 skipped, flattening the bright excess 0.4 pm → 0.0 pm while touching ~2% of pixels. The step method is unchanged (sharp-scar detector).

2. Sub-pixel Gaussian blur + physical readout

The "Smooth: Gaussian" control was integer-only (1–20 px) with no feedback on the kernel's physical extent. Now: float σ 0.2–20.0 px in 0.1 steps, and a readout showing σ/FWHM/kernel-extent in nm (FWHM = 2.3548·σ; kernel ±4σ, matching scipy truncate=4.0). GUI-only — the backend already accepted float σ. See format_gaussian_readout and set_pixel_size_nm in probeflow/gui/processing.py.

Tests

New/updated regression tests in tests/test_processing.py and tests/test_gui_processing_panel.py; full suite 254 passed, ruff clean.

🤖 Generated with Claude Code

jacobson30-bot and others added 2 commits June 15, 2026 12:35
…adout

The image-viewer "Smooth: Gaussian" control was an integer slider (1-20 px) and
reported nothing about the physical extent of the kernel, so a user could not
tell what distance was being blurred or do gentle sub-pixel smoothing. The blur
itself (scipy gaussian_filter, sigma in px, kernel truncated at +-4 sigma) is
sound and ~equivalent to ImageJ for sigma >= 1 px on float data; the gap was the
GUI control and units feedback. Backend already accepts float sigma, so this is
GUI-only.

Where to look:
- probeflow/gui/processing.py:
  * format_gaussian_readout(sigma_px, px_nm) — pure, unit-tested formatter
    (FWHM = 2.3548*sigma; kernel half-width = int(4*sigma+0.5), matching scipy
    truncate=4.0).
  * _sub_slider() gained a `scale` arg; the smooth slider now spans 0.2-20.0 px
    in 0.1 steps (_SMOOTH_SIGMA_SCALE). state()/set_state() convert accordingly.
  * set_pixel_size_nm() + _update_smooth_readout() drive a readout label that
    shows sigma/FWHM/kernel extent in nm (or px-only when uncalibrated).
- probeflow/gui/dialogs/image_viewer.py: _refresh_display_array pushes the loaded
  scan's pixel size into the panel via set_pixel_size_nm.

Tests: test_format_gaussian_readout_pure, test_viewer_full_smooth_sigma_is_subpixel_float.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…xtended lines

The MAD/outlier bad-line method failed on real STM/AFM scans (createc qPlus AFM)
in three compounding ways. All three are fixed in probeflow/processing/bad_lines.py
(detect_bad_scanline_segments MAD branch + helpers).

1. Correction could not be applied. _split_segments_by_adjacent_limit was
   column-blind: a line counted as "bad" if it had a segment anywhere, so noise
   across many lines formed one long consecutive-line run that exceeded
   "max adjacent" and ALL segments were skipped (nothing repaired). It is now
   column-overlap-aware (union-find over segments linked only when adjacent rows
   overlap in columns); only a genuine vertical stack taller than the limit is
   skipped. The repair itself was already column-aware.

2. Long segments were invisible/shattered. The method subtracted each row's own
   median before thresholding, so a defect covering >~half the row became the
   row median and vanished (lowering the threshold only surfaced noise). It now
   thresholds the neighbour-referenced residual directly, bridges short noise
   gaps (_close_small_gaps, _NOISE_GAP_TOL_PX) and keeps a run only if it is a
   defect on its MEDIAN — robust to per-pixel noise; no upper-length cap.

3. Low-contrast lines were undetectable. The real bright lines are only ~1.5
   sigma per pixel, so per-pixel thresholding can't separate them from texture.
   The residual is now smoothed along each row first (_smooth_rows_nanaware,
   _MAD_SMOOTH_WINDOW_PX = 11) as a matched filter for extended defects: SNR
   gain ~sqrt(window) lets a faint-but-long line clear a clean threshold while
   texture averages out. On the sample file, threshold-5 detection went from 0
   to ~80 segments on exactly the bright rows, repaired with 0 skipped. The
   `step` method is unchanged and remains the sharp/short-scar detector.

Tests: test_adjacent_limit_is_column_aware,
test_mad_detects_long_partial_segment_against_noise,
test_mad_detects_whole_line_offset_not_just_internal_segments, and an updated
(now matched-filter-aware) test_mad_outlier_method_repairs_only_segment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jacobson30-bot jacobson30-bot merged commit 3dedb0f into main Jun 15, 2026
3 checks passed
@jacobson30-bot jacobson30-bot deleted the fix/bad-lines-and-gaussian-blur branch June 15, 2026 05:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant