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:
ValueErrorif 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:
- Pull
doc.image(float32), normalize if max>1. - Decide rect: from
rect_normorauto_rect_50x50. - Run BN →
out. - If a destination mask exists via
_active_mask_array_from_doc(doc), blendbase*(1-m)+out*m. doc.apply_edit(...)with metadata{step_name: "Background Neutralization", preset: ...}.
- Pull
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)(frompro.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()