Halo-B-Gon

Attenuates diffuse haloes around bright stars in a stars-only image. Built for the halo patterns typical of achromatic and semi-apo refractors, uncorrected spherochromatism, and dew- or scatter-induced glows — the big soft rings and bluish halos that nothing else wants to deal with.

Halo-B-Gon is a targeted retouch, not a generic denoiser or deconvolution. It operates on stars-only images (the output of Remove Stars), finds concentric ring-like structure around point sources, and darkens it. The star cores themselves are preserved.

1. Where it fits in the workflow

The standard sequence is:

  1. Remove Stars on the stretched master, producing a stars-only companion image and a starless image.
  2. Halo-B-Gon on the stars-only image to tame the halo rings.
  3. Add Stars to screen the retouched stars back onto the finished starless image.

Running Halo-B-Gon on a full starless+stars image is not what the math is tuned for — the ring-detector will fire on nebular edges too, and the gamma pass will darken everything. Keep it to the stars image.

Why it works best on refractor data. Achromatic and semi-apo refractors can leave big, soft, often blue or violet rings around bright stars because secondary-spectrum focus differs between wavelengths. Those rings are extended light that the star removal step kept with the stars layer. Halo-B-Gon targets the concentric spatial pattern directly, so it attenuates them without touching the star cores.

2. Controls

2.1 Reduction level

A slider with four discrete settings:

Each level affects two things at once: the strength of the ring-detection mask that gets multiplied into the image (0.33 × level, so Extra Low = 0, Medium = 0.66), and a final darkening gamma curve (γ = 1.2, 1.5, 1.8, 2.2 across the four levels). The two effects compound — stepping up from Low to Medium is a real jump, not a small tweak.

2.2 Linear data

Check this if you are running Halo-B-Gon on linear data (data that has not been stretched). The algorithm's unsharp step is scale-sensitive and designed for display-range intensities, so for linear data the dialog applies a x1/5 pre-pass to lift the shadows into a workable range before running the main pipeline. For stretched data this box stays off.

2.3 Apply to

The dialog stays open after each Apply and refreshes its preview to whatever document is now active, so you can keep processing other images without reopening the tool.

3. Preview

The preview panel shows the current result in real time:

Preview and Apply run the same code path — what you see is exactly what you get.

4. Mask integration

If the active document has a mask active, Halo-B-Gon's output is blended with the untouched original through that mask on Apply: the halo reduction only lands where the mask is bright. This lets you process just one quadrant of a mosaic, or protect a nebular region that happens to have ring-like structure the algorithm would otherwise dim. The preview does not account for the mask; it shows the raw algorithm output.

5. Buttons

6. Preset drag handle

At the lower-left corner the grip icon lets you drag the current settings out as a reusable preset: drop it on the shortcut bar to make a one-click Halo-B-Gon button with these exact parameters, or drop it directly onto an open image to apply them headlessly without opening this dialog. The preset carries reduction level and the Linear data flag; the output-target choice (overwrite / new view) is a session-level preference and is deliberately not part of the preset.

7. Tips

8. Technical details

Direct port of the SASv2 HaloProcessingThread.applyHaloReduction implementation. All processing on float32 in [0, 1]; mono stays mono, RGB stays RGB. Where OpenCV is used (cvtColor, GaussianBlur, addWeighted, multiply, LUT) Halo-B-Gon uses it — this is a tuned algorithm with specific numeric constants rather than a parameter-rich one.

8.1 Optional linear pre-pass

When Linear data is checked, the pipeline first lifts intensities with a strong gamma:

x' \;=\; x^{1/5}

bringing shadows into the mid-range the unsharp step was tuned for. The rest of the pipeline then runs on x' and the result is kept in that space — there is no inverse gamma at the end, because Halo-B-Gon's final gamma LUT already provides shaping.

8.2 Lightness channel

From the (possibly pre-passed) working image, the pipeline builds a single-channel lightness:

L \;=\; \frac{\mathrm{gray}(I)}{255}

For RGB this is the OpenCV COLOR_RGB2GRAY conversion; for mono input it uses the plane directly. The division by 255 looks surprising on a float image already in [0, 1] — the result lies in [0, 1/255] — and it is load-bearing: the whole pipeline is calibrated around these small values. Changing the normalisation changes the mask strength and would require re-tuning the 1.66, 0.66, and 0.33 constants below. The /255 is kept intentionally for byte-exact match with SASv2.

8.3 Unsharp mask of the lightness

Build an unsharp version of the lightness with a Gaussian blur:

U \;=\; 1.66\,L \;-\; 0.66\,G_{\sigma}(L)

where Gσ is the Gaussian blur at σ = 2. This emphasises point-source centres and damps smooth gradients — which is exactly what you need to tell halo rings apart from the surrounding sky.

8.4 Enhanced mask (the ring detector)

The core trick:

M \;=\; (1 - U) \;-\; 0.33\,k \,\cdot\, G_{\sigma}(U)

where k is the reduction level (0, 1, 2, 3). Term by term:

At level 0 (k = 0) the mask reduces to (1 − U), so only the final gamma pass darkens the image. From level 1 onward, the ring-detector contribution kicks in and halo structure is actively suppressed.

8.5 Multiplicative application

I_{\mathrm{halo}} \;=\; I \,\cdot\, M

The mask broadcasts across colour channels (so all three RGB channels get the same spatial correction). Because the mask is close to 1 almost everywhere and dips at halos, multiplying leaves most of the image alone and darkens exactly where halos were.

8.6 Final gamma curve

A per-level gamma curve applied through an 8-bit LUT finishes the job:

\mathrm{LUT}(x) \;=\; x^{\gamma_k}

with γ drawn from the fixed set {1.2, 1.5, 1.8, 2.2}: γ = 1.2 at Extra Low, up to γ = 2.2 at High. Every γ is > 1, so the curve darkens mid-tones; the higher the level, the harder the darkening. This provides a global contrast boost on top of the ring-targeted reduction and is what gives the different levels their perceivable step in strength.

8.7 Final composition

I_{\mathrm{out}} \;=\; \mathrm{LUT}\!\left(I \,\cdot\, M\right)

and, if a document mask is active, the result is blended back through it (output in the mask's bright regions, original everywhere else).

8.8 Shape preservation

Input shape round-trips exactly: a 2-D mono image comes out 2-D, a 3-D single-channel image (H×W×1) stays that shape, and RGB stays RGB. The mask is replicated across channels for RGB input; for mono it stays 2-D. The output is clipped to [0, 1] and returned as float32.

8.9 Headless replay

The function apply_halo_b_gon_to_doc(parent, doc, preset) performs the entire pipeline from a preset dict:

This is what the preset drag handle registers for Replay, and what drops of the handle onto an image invoke directly without opening the dialog.

Credits. Halo-B-Gon is SASpro's own algorithm, inherited from SASv2 and ported byte-exactly here. The ring-detection approach (combine an unsharp mask of the lightness with a Gaussian-blurred copy of itself, then multiply the result into the image) is tuned numerically rather than published formally — the specific constants 1.66, 0.66, 0.33, and the four γ values are the result of trial-and-error against real refractor data over multiple SASv2 releases. The pipeline uses OpenCV primitives (cvtColor, GaussianBlur, addWeighted, multiply, LUT) for the heavy per-pixel work.