M74 — transparent cutouts for slides & stamps#

M74 transparent RGBA stamp — the page background shows through the sky

That image above has no background — it is a genuine RGBA PNG, and the docs page shows through where the sky used to be. Toggle the site light/dark theme and watch the “sky” follow. That is the whole point: one export that drops onto any slide, poster, or web page.

M74 (NGC 628) here is an SDSS gri composite. Any RGB composite works — including ones made entirely outside multicolorfits.

You will learn how to:

  • turn an RGB composite into a transparent-sky RGBA cutout (make_transparent_cutout),

  • control the fade with alpha_lo / alpha_hi / alpha_gamma,

  • preview against checkerboard / dark / light backgrounds before exporting,

  • export straight from a session (export_transparent_cutout) or the GUI,

  • recover RGBA from an image already flattened onto a solid color (deblend_background — the exact inverse operation).

Concepts: Saving outputs. Related: white / transparent combine backgrounds in Color compositing.


From composite to cutout#

import multicolorfits as mcf

# rgb: any (ny, nx, 3) float composite in [0, 1] — here from a session.
# gri mapped straight to R/G/B channels ≈ true color: blue arms, warm core.
s = mcf.McfSession(n_panels=3)
s.load_files(['m74_sdss_i.fits', 'm74_sdss_r.fits', 'm74_sdss_g.fits'],
             colors=['#FF0000', '#00FF00', '#0000FF'],
             labels=['i', 'r', 'g'])
for p in s.panels:
    p.stretch = 'asinh'
    p.set_percentiles(30.0, 99.5)
rgb = s.render_combined()

rgba = mcf.make_transparent_cutout(
    rgb,
    alpha_smooth=3,             # blur the matte's intensity map (px) —
                                #   follow whole arms, not pixel noise
    alpha_hi=90,                # opacity threshold: this bright and up = opaque
    alpha_lo=60,                # fully transparent at/below this percentile
    alpha_gamma=0.4,            # fade shape between them (<1 = bolder body)
    crop='auto', pad=0.06,      # trim empty sky, keep a 6% margin
)
mcf.save_transparent_cutout(rgba, 'm74_stamp.png')

The matte is a thresholded fade built from the composite’s luminance: everything at or above the alpha_hi percentile is fully opaque, and alpha ramps down (shaped by alpha_gamma) to fully transparent at alpha_lo. crop='auto' then trims the frame to the non-transparent content, and size= (e.g. size=512) downsamples for a web-friendly stamp.

Three things make this recipe hold up on a white slide:

  1. Channel colors — if every band gets a warm/near-white tint, the composite itself is white-ish and no alpha trick will make it read against white. Near-true-color channels keep the arms visibly blue.

  2. alpha_smooth — a per-pixel matte lets noise punch pinholes through the arms, so they never read as solid. Blurring the intensity map (the RGB data is untouched) by a few pixels makes the matte trace coherent structure: whole arms cross the threshold together and stay opaque.

  3. The threshold placementalpha_hi=90 puts full opacity at the arm level rather than only the core, while alpha_lo=60 cuts the smoothed sky to exactly zero, so no faint haze rectangle outlines the frame when the stamp is layered over other content.

Check it before you ship it#

fig = mcf.preview_cutout_on_backgrounds(rgba)   # checkerboard / dark / light
fig.savefig('m74_preview.png', dpi=110)
M74 cutout previewed over checkerboard, dark, and light backgrounds M74 cutout previewed over checkerboard, dark, and light backgrounds (dark chrome)

The light-background panel is the acid test: faint outer structure that looks fine on black can read as smudge on a white slide.

Shaping the fade — alpha_gamma#

alpha_gamma accepts any value above zero and shapes the fade between the alpha_lo and alpha_hi percentiles. Below 1 it bends the ramp so mid-brightness disk pixels stay mostly opaque; 1 is a linear fade that thins the whole object; above 1 it works the other way, tightening the matte so only the brightest structure — the arm ridges and core here — gets through at high opacity:

M74 over a light background — alpha_gamma 0.4 (bold body), 1.0 (linear fade), 1.4 (tight matte) M74 alpha_gamma comparison (dark chrome)

Rules of thumb:

  • dark decks — linear (alpha_gamma=1) often looks airier;

  • light decks / printalpha_gamma≈0.3–0.5 keeps the body solid;

  • busy backgrounds / small stampsalpha_gamma>1 (e.g. 1.4) trims the faint skirt so just the crisp bright skeleton overlays cleanly;

  • crank alpha_lo up if sky noise survives (or a faint haze box outlines the frame when overlaid); lower alpha_hi to push full opacity further out from the core into the arms;

  • raise alpha_smooth for softer, more coherent outlines (it also rounds the cutout rim — drop it back toward 0 if you want crisp per-pixel detail at the edge).

Other useful knobs: matte='circle' / 'ellipse' for a geometric window, soft_edge= for a Gaussian-feathered rim on the finished alpha, alpha_source='layer' to drive the matte from one specific band, and for composites that don’t sit on black, alpha_source='dist' with sky_color='white' (or any color) mattes by distance from the background color — dark features survive a white-background cutout, where plain luminance would erase them (invert=True is the simple version for grey-scale-like cases).

One-liners from a session or the GUI#

# Straight from the session — renders transparent, mattes, crops, saves:
s.export_transparent_cutout(size=512, savepath='m74_stamp.png',
                            alpha_smooth=3, alpha_lo=60, alpha_hi=90,
                            alpha_gamma=0.4, crop='auto')

# Batch a set of composites with shared settings:
stamps = mcf.batch_transparent_cutouts([rgb1, rgb2, rgb3],
                                       alpha_smooth=3, alpha_gamma=0.4,
                                       size=256)

Both GUIs expose the same export as Export transparent… (web: POST /api/export/cutout).

Tip

Transparent cutout vs transparent background: combine_background = 'transparent' (see Color compositing) makes the compose canvas transparent but keeps the full frame. make_transparent_cutout additionally mattes the sky inside the image and crops — that’s what you want for stamps and overlay art.

The reverse direction — deblending a flattened image#

make_transparent_cutout estimates a matte from brightness. Its sibling deblend_background solves for one: if an image was already flattened onto a known solid color (a JPEG/PNG figure on white, a screenshot on a slide background), the compositing equation (C = \alpha F + (1-\alpha) B) can be inverted per pixel for the minimum (\alpha) whose foreground stays inside the RGB gamut — a closed-form, fully vectorized solve that works for any background color, and keeps dark features that a luminance matte would erase:

flat = plt.imread('figure_on_white.png')[..., :3]   # any flattened RGB
rgba = mcf.deblend_background(flat, background='white')   # or any color
mcf.save_transparent_cutout(rgba, 'recovered.png')

# Round trip is exact (up to 8-bit quantization, absorbed by tol=):
back = mcf.flatten_rgba(rgba, background='white')
M74 stamp flattened onto a solid color, the recovered alpha map, and the re-flattened check M74 deblend round trip (dark chrome)

The middle panel is the recovered alpha — solved, not guessed — and re-flattening the result onto the same color reproduces the input to within one 8-bit step. The same matte-shaping knobs from above (alpha_smooth, alpha_lo / alpha_hi thresholding, alpha_gamma, matte, soft_edge, crop, size) are available on deblend_background too; the defaults keep the physically exact alpha, and the knobs trade exactness for presentation control.

Note

The recovered foreground uses the standard minimum-alpha convention (maximally transparent, as in color-keying tools) — semi-transparent pixels come back with their color pushed to the gamut boundary. Re-flattening onto any background is still correct; only pixel-level “what was the original foreground color” is fundamentally unrecoverable (3 equations, 4 unknowns).