Function: Background Neutralization - setiastro/setiastrosuitepro GitHub Wiki

Background Neutralization (RGB)

A fast, robust background neutralization (BN) utility for RGB images. Supports headless operation for pipeline steps and an interactive PyQt dialog for manual selection and preview.


What it does

  • Neutralizes background color cast in RGB float32 images scaled to [0,1].

  • Uses either:

    • a user-drawn sample rectangle, or
    • an auto-selected 50×50 patch found by a luminance-based search.
  • Respects an active destination mask (blend only where mask=1).

  • Optional Auto-Stretch in the preview for easier visual judging (display-only).


Key APIs

background_neutralize_rgb(img, rect_xywh) -> np.ndarray

  • Input: img = RGB float32 in [0,1]; rect_xywh = (x, y, w, h) in image pixels.
  • Method: Compute channel medians over the sample rect; shift/scale each channel so medians match their average (guarded by small epsilon).
  • Output: New RGB float32 image in [0,1].
  • Raises: ValueError if the image is not 3-channel RGB.

Auto background finder

  • _find_best_patch_center(lum): luminance-plane search (coarse 10×10 tiling + downhill walks) to locate low-background zones.
  • auto_rect_50x50(img_rgb) -> (x, y, 50, 50): returns a 50×50 patch centered on the best candidate, clamped to ≥100 px margins and refined locally.

Headless entrypoint

apply_background_neutral_to_doc(doc, preset: dict | None)

  • Preset schema:

    {
      "mode": "auto" | "rect",
      "rect_norm": [x0, y0, w, h]   // normalized 0..1, required for "rect"
    }
    
  • Flow:

    1. Pull doc.image (float32), normalize if max>1.
    2. Decide rect: from rect_norm or auto_rect_50x50.
    3. Run BN → out.
    4. If a destination mask exists via _active_mask_array_from_doc(doc), blend base*(1-m)+out*m.
    5. doc.apply_edit(...) with metadata {step_name: "Background Neutralization", preset: ...}.

Note: Headless and dialog modes both require RGB; mono images are not supported here.


Interactive dialog

BackgroundNeutralizationDialog(parent, doc, icon=None)

UI & Controls

  • Canvas: Image shown in a QGraphicsView/QGraphicsScene, true pixel coordinates.

  • Draw a sample: Left-drag to create a rectangle (min size enforced).

    • Green dashed while dragging, solid red when set.
  • Find Background: Auto-picks a 50×50 patch (gold rectangle) using the auto finder.

  • Auto-Stretch (preview-only): Toggles contrast enhancement (uses stretch_color_image(..., linked=False)).

  • Apply Neutralization: Runs BN using the current rectangle; blends with active mask (if any); records metadata.

  • Zoom: Zoom In / Fit to View / Zoom Out. Mouse handling keeps the scene rect equal to image pixels for 1:1 mapping.

  • Cancel: Close without applying.

Behavior details

  • Images are defensively normalized to [0,1] for display and processing.
  • If a selection is too small, a warning prompts the user to redraw.
  • When Auto-Stretch is toggled, the scene is rebuilt to reflect the new display transform.

Integration points

  • Masking: _active_mask_array_from_doc(doc) (from pro.add_stars) supplies a binary/float mask on the destination; BN output is blended accordingly.
  • Preview stretch: imageops.stretch.stretch_color_image (linked=False) for display-only preview.
  • Document I/O: doc.apply_edit(array, metadata=..., step_name="Background Neutralization").

Assumptions & constraints

  • Input type: 3-channel RGB (float32) scaled to **[0,1]`.
  • Mono images: not supported in this module (convert upstream if needed).
  • Rect coordinates: image-space pixels; dialog converts scene → image rect exactly (scene rect = image bounds).
  • Performance: Operations are vectorized; sample medians and per-channel transforms are O(N) in image size.

Error handling & UX

  • Warns if: no image, selection too small, or not RGB.
  • Clamps invalid rectangles to image bounds; enforces (w,h)≥1.
  • Epsilon safeguards division when sample medians are nearly equal.

Tips & troubleshooting

  • Patch choice matters: Pick star-free, gradient-representative background (the auto finder prefers low-median luminance areas).
  • Use mask for targeted fixes: Paint a destination mask for sky-only neutralization; bright targets remain untouched.
  • Preview ≠ data: Auto-Stretch only changes the dialog preview, not the data written back.
  • Normalization: If your pipeline uses other ranges, normalize to [0,1] before calling BN (headless already guards for max>1).

Metadata written

  • Headless: {"step_name": "Background Neutralization", "preset": {...}}
  • Dialog: {"step_name": "Background Neutralization", "rect": (x, y, w, h)}

Minimal usage examples (pseudocode)

Headless (auto):

apply_background_neutral_to_doc(doc, {"mode": "auto"})

Headless (explicit rect, normalized coords):

apply_background_neutral_to_doc(doc, {"mode": "rect", "rect_norm": [0.35, 0.40, 0.08, 0.08]})

Interactive:

dlg = BackgroundNeutralizationDialog(parent, doc)
dlg.exec()