A gallery-grid palette browser for narrowband data. Load your Ha / OIII / SII (or OSC dual-band equivalents), click Create Palettes, and 18 standard and specialty palette mappings render side-by-side. Click any thumbnail to blow it up in the main preview, nudge the per-channel intensities to taste, and push the final to a new document.
PPP was built around a specific frustration: most tools make you pick a palette first, then look at the result, then pick a different one if you don't like it — a workflow that's bad at helping you discover the surprising combination that fits your data. Showing every palette at once collapses that into one glance.
Dynamic Inverse is Frank's own palette and appears only here. The other 17 palettes are the industry-standard permutations plus the published Foraxx algorithm (see § 13.4). See the credit block at the bottom.
Two windows:
Keeping the gallery in its own window lets you position it on a second monitor or beside the main preview without one eating the other's space.
Five load slots on the left:
Each load dialog asks whether to pull From View (any currently open SASpro document) or From File (PNG, TIF, FITS, XISF). The green status line under each slot confirms the loaded source. Minimum requirement is any two of the three narrowband channels, or one OSC image.
The Linear input (apply statistical stretch before build) checkbox at the top of the controls governs whether PPP stretches each loaded channel before combining them. For stretched data turn this off. For linear sub-stacks straight out of integration leave it on (the default): PPP runs its usual statistical stretch at target median 0.25 on each channel individually before building the palettes, so the result is a usable display image rather than an inky black square.
Toggling this at any time rebuilds the cached stretched versions of every loaded channel — the next palette render uses them immediately. The setting persists across sessions.
Both OSC slots split the loaded RGB into pseudo-narrowband channels and either seed or blend with existing loaded data:
Ha ≈ R and
OIII ≈ mean(G, B).SII ≈ R and
OIII ≈ mean(G, B).If one of the narrowband slots is also loaded (say you have a dedicated Ha mono alongside your OSC1 data), PPP blends them 50 / 50 rather than discarding either. This gives you a clean upgrade path from OSC-only to OSC-plus-mono-narrowband without rebuilding the project.
If the loaded channels aren't the same pixel size — different cameras, cropped files, or OSC frames at a different scale from mono — PPP offers to resize everything to a reference frame (preferred: Ha, then OIII, then SII). You get asked once per session. The resize is a simple spatial warp with no plate-solving alignment, so for serious work you want to register your channels upstream first; the resize is a convenience for lazy quick looks and for dropping in a lower-resolution Ha into a higher-resolution OIII / SII pair.
The Create Palettes button builds a thumbnail for every palette in the gallery grid. Each thumbnail is a half-size view of the full frame (so the full composition is visible, not just a centre crop) with the palette name painted on a translucent strip at the bottom. The selected palette's name turns green.
Clicking any thumbnail does two things:
Switching between palettes is fast because PPP uses the already-cached (and possibly stretched) channel buffers. There's no need to re-click Create Palettes between selections.
Pan and zoom in the main preview are preserved when you switch palettes. This is the whole point: zoom in on the Running Man, click through SHO → HOO → Foraxx → Dynamic Inverse, and you're looking at the same patch each time, so differences in colour balance jump out. Only the first palette you generate fits automatically to the preview; everything after reuses whatever zoom and pan state you were already in.
Fourteen three-letter palettes assigning the three narrowband channels to R / G / B in different orders:
| Name | R | G | B | Notes |
|---|---|---|---|---|
| SHO | SII | Ha | OIII | “Hubble palette.” The classic. |
| HOO | Ha | OIII | OIII | Bicolour, Ha+OIII. Workhorse for Ha+OIII datasets. |
| HOS | Ha | OIII | SII | Ha-forward natural-ish rendering. |
| HSO | Ha | SII | OIII | Ha red, SII green, OIII blue. |
| HSS | Ha | SII | SII | Bicolour, warm. Yellows the SII regions. |
| HHO | Ha | Ha | OIII | Bicolour Ha-forward, OIII in blue. |
| OHH | OIII | Ha | Ha | OIII red, bicolour warm shadows. |
| OHS | OIII | Ha | SII | OIII-forward with SII in blue. |
| OOS | OIII | OIII | SII | Cool bicolour, OIII-forward. |
| OSH | OIII | SII | Ha | Rotated SHO; Ha becomes blue. |
| OSS | OIII | SII | SII | OIII red, SII dominant in green and blue. |
| SHH | SII | Ha | Ha | SII-red bicolour. |
| SOH | SII | OIII | Ha | Completes the six true permutations. |
| SOO | SII | OIII | OIII | SII-red / OIII bicolour. |
With only two of the three narrowband channels available, PPP substitutes the missing one from the available ones so every thumbnail still renders. The substitution rules are documented in § 13.1.
Weighted blends aimed at approximating broadband-like colour from narrowband data:
R = (Ha + SII) / 2,
G = 0.3·Ha + 0.7·OIII,
B = 0.9·OIII + 0.1·Ha.R = 0.7·Ha + 0.3·SII,
G = 0.3·SII + 0.7·OIII,
B = OIII.Neither is a true LRGB combine; both bias OIII into the blue channel and spread Ha across red and green to approximate a natural-looking Hα emission in a red / orange hue.
Dynamic-gated palette that automatically switches character based on whether both Ha and OIII (or all three) are strong at a given pixel. Produces smooth, hue-aware blends without hard thresholds. Works in both bicolour (Ha + OIII) and tricolour (Ha + OIII + SII) modes. See § 13.4 for the math.
Frank's own palette, exclusive to SASpro. Uses the Foraxx-style gate function but applies it inversely: Ha and SII swap roles based on OIII presence, and the blue channel is driven by OIII weighted against SII absence. Tends to produce a cooler, more cyan / violet appearance than SHO or Foraxx, which can reveal structure in OIII-dominant regions that the standard palettes flatten. See § 13.5 for the full algorithm.
Once you've picked a palette, click Push Final to New View. This opens the Adjust Palette Intensities dialog:
This is where you tune the balance between emission lines without having to rebuild upstream: if the Dynamic Inverse looks great but SII is overpowering the midtones, drop its slider to ~60% and you've calibrated the final composite in two seconds.
Workflow suggestion. Create Palettes once, click through every thumbnail that looks promising at a glance, pin the two or three favourites, then run Push Final on each with the intensity sliders fine-tuned. You'll have three polished versions of the same object in different palettes within a few minutes, ready to compare side-by-side at the end.
Saved to QSettings under the perfect_palette_picker/
namespace:
All channels normalised to float32 in [0, 1]. Where cv2 is available it handles resize and interpolation; a PIL fallback kicks in on systems without it. The palette algorithms are pure NumPy; the stretch functions use SASpro's standard statistical stretch at target median 0.25.
When only two of Ha / OIII / SII are loaded, PPP fills the missing one at palette-build time so every three-letter permutation still renders:
Substitution happens after OSC synthesis and after
stretch, so a palette like HOS run on Ha+OIII only (no SII) uses
SII := Ha, giving a result that is really a
tinted bicolour but reads as a tricolour. The thumbnails make this
visually obvious: palettes that depend on the missing channel look
flat relative to those that don't.
OSC filters (Optolong L-eXtreme, L-eNhance, IDAS NBZ, Antlia ALP-T, etc.) deliver two emission lines on a Bayer sensor. The red site captures the long-wavelength line; the green and blue sites share the shorter-wavelength line because both are inside the filter's second passband. PPP exploits this:
and symmetrically for OSC2 with SII in place of Ha. When a mono channel is also loaded alongside the OSC, the two signals are averaged:
This averaging is deliberately simple: the user who went to the trouble of adding a dedicated mono panel probably wants that signal to count equally, not dominate or be dominated by the OSC estimate.
When Linear input is checked, each loaded channel is run through
SASpro's own statistical stretch at a target median of 0.25
(stretch_mono_image for narrowband,
stretch_color_image with linked=False for
OSC). The stretched buffer is cached per channel; it's rebuilt only
when either the Linear toggle changes or the user loads a new
channel. Palette construction always uses whatever cache is current,
so switching Linear on and off is essentially free after the first
build.
At the heart of Foraxx is a dynamic gate function:
on x ∈ [0, 1]. Properties:
g(0) = 0, g(1) = 1,
and g(x) stays high across the midrange (for example
g(0.5) ≈ 0.707) with a sharp drop only
near zero. Used as a blend weight, it produces hue-aware transitions
that are smooth where the signal is present and sharp where it isn't.
Tricolour (Ha + OIII + SII):
Bicolour (Ha + OIII, no SII):
PPP picks between tricolour and bicolour automatically based on whether a real SII channel was loaded (not a substituted one). If only two of Ha / OIII are present with a dummy SII substitution, the bicolour form runs so you don't get a smeared-SII artefact.
Dynamic Inverse reuses the Foraxx gate but applies it to a different structural question: “where OIII is absent, emphasise SII; where OIII is present, emphasise Ha.” This inverts the usual SHO logic where SII sits in red regardless of what OIII is doing.
Interpretation:
The visual effect is a palette that reads more like a true tri-colour photograph than SHO does — emission regions keep distinct hues even when they overlap — while the inverse logic in the red channel reveals structure that gets buried in flat SHO.
Thumbnails are built from a half-size view of the full rectangle, not a centre crop. The first step resizes every loaded channel to the reference frame's full dimensions; the second step scales uniformly to 50% of width and height. The result is a 220×110 button icon that shows the whole composition at reduced resolution, so you can see edge features, nebular extent, and overall balance — information lost in a centre crop. This is why clicking a thumbnail that looks subtle in the gallery can reveal a very striking full-resolution composition in the main preview.
When you click a different thumbnail, PPP captures the current viewport centre as a fraction of the base image size and the current zoom level, builds the new palette, and restores both. Because the palette outputs always share the same dimensions and spatial layout (they're the same channels combined differently), the restored view lands on exactly the same patch of sky. Only the first build fits-to-view automatically.
Push Final doesn't just send the current preview — it opens the Adjust Palette Intensities dialog with sliders backed by the same prepared Ha / OIII / SII buffers the palette build used. The sliders multiply each channel before the palette formula runs, and the formula runs again from scratch on every slider move (debounced at 300 ms). The “Accept” path writes the adjusted result back into the main preview and creates a new document through DocManager with the palette name as the title. If DocManager isn't available (unusual — standalone usage), a basic viewer window is opened as a fallback.
x1−x) is
the published creation of Bob Franke / “Foraxx” in
the astrophotography community, implemented here under its original
public specification. The 14 three-letter palette permutations
(SHO, HOO, HSO, …) are the industry-standard
narrowband conventions dating back to the Hubble Space Telescope
imaging team's work; the specific set and naming follow general
community practice. The Realistic1 and Realistic2 weighted
blends are classical mixing formulas with slight custom weight choices
in SASpro. Image loading uses SASpro's
legacy.image_manager.load_image; stretching uses
SASpro's own stretch_mono_image and
stretch_color_image. OSC filter examples
(Optolong L-eXtreme / L-eNhance, IDAS NBZ, Antlia ALP-T) are
third-party products referenced for context only.