Background Extraction · for Siril

MEMBRANE

The stiffest surface that still honours the sky.

A background model that removes light pollution, sky glow and vignetting from your frames without flattening the nebulosity you spent the night collecting. Runs inside Siril, on the image you already have open.

Version1.1.0
LicenseFree public beta
RequiresSiril 1.2+ · Python
The idea

Not a curve fit. A physical surface.

Every other background extractor asks the same question: which formula best passes through my sky samples? MEMBRANE asks a different one. It stretches an elastic sheet across the frame and lets the sky samples pull on it, then finds the shape where the sheet stops moving. Nothing forces it into a polynomial, and nothing lets it wobble between samples.

01

Free of shape priors

A polynomial can only bend the way its degree allows. Amp glow in one corner plus a diagonal gradient is already too much for it. MEMBRANE has no preferred shape, so it follows whatever your sky actually did.

02

Cost independent of samples

Spline methods solve a dense system that grows with the cube of the sample count, so a fine sampling grid becomes painful. MEMBRANE solves coarse-to-fine on a pyramid; its cost tracks the grid, not the samples.

03

Gentle where it matters

Where an object is masked out, the sheet simply spans the hole instead of extrapolating into it. That is why bright targets come out with their signal intact rather than sitting in a subtracted bowl.

Version 1.1

Same sheet, better evidence.

1.0 rejected a sky sample by comparing it to a plane fitted across the whole frame, so any real curvature in the light pollution looked like an object and was thrown away. 1.1 compares each sample to its own neighbourhood instead, then re-weights the fit against its own residual so bright targets lose their vote without bright sky losing it too.

Sampling

A local horizon

Rejection now runs against a robust local trend of the tile field, not a global plane. A bright corner, an off-frame moon or an airglow band stays in the model instead of being discarded as contamination.

Fitting

It argues with itself

The surface is solved, then every cell sitting above it is demoted and the surface is solved again. A cell below the surface keeps its vote, because that is noise, not nebulosity. Nothing is ever silenced completely, so genuinely bright sky can still bend the sheet.

Edges

No bowed rim

The relaxation used to be forced flat exactly at the frame border, and a real gradient is not flat there. The solve now runs on a grid that extends past the image, so the fit keeps its slope right to the edge.

Model Time Residual sky Edge bias Target signal kept
MEMBRANE 1.1 509 ms 36.9 ADU -0.28 σ 94.9%
MEMBRANE 1.0 184 ms 40.5 ADU -0.34 σ 95.9%
RBF (thin-plate spline) 254 ms 70.1 ADU -0.28 σ 94.0%
Polynomial (degree 4) 113 ms 78.3 ADU -0.24 σ 93.7%

M8, the single 120 s CFA sub, modelled per Bayer sub-channel. Uncorrected sky spread 338 ADU, uncorrected edge bias −0.90 σ. Residual sky is the spread of the robust tile sky levels with target tiles excluded, so a model cannot score well here by eating the nebula: that shows up in the last column instead.

M8 sub corrected by MEMBRANE 1.1, MEMBRANE 1.0, RBF and a polynomial
Top row: the same sub under one identical stretch. Bottom row: the sky component alone, on a shared scale, with the stars and the noise filtered out. The blue frame around the original is the vignette; RBF and the polynomial still leave it in the corners, 1.1 leaves the least of it and holds the Lagoon at the same time.
Model Time Residual sky Edge bias Target signal kept
MEMBRANE 1.1 755 ms 10.7 ppm 0.00 σ 100.1%
MEMBRANE 1.0 495 ms 11.9 ppm -0.00 σ 100.0%
RBF (thin-plate spline) 889 ms 19.4 ppm 0.01 σ 100.2%
Polynomial (degree 4) 261 ms 19.4 ppm 0.03 σ 100.2%

M57, the 3008 × 3008 stack, mean of the three colour channels. Uncorrected sky spread 163 ppm. The target is small here, so every model returns it intact and the frame only separates them on the sky.

M57 stack corrected by MEMBRANE 1.1, MEMBRANE 1.0, RBF and a polynomial
The same layout on the stack. The uncorrected sky splits the frame in two; both MEMBRANE versions clear it, while RBF and the polynomial keep a blue band down the left edge. Every model returns the ring itself untouched.

One number moved the wrong way: on the nebula-filled sub, 1.0 keeps about one percent more of the target than 1.1, because its blunter rejection happened to shield the Lagoon. Raising the smoothing to 0.01 puts that back and still leaves 1.1 flatter than RBF or the polynomial. The new "object protection" control moves the same trade-off in the other direction.

Every setting, both frames

The whole sweep, not the flattering row.

92 runs: each model against each setting, on both frames, measured identically. Pick a frame and a parameter to see how the three models respond. Best value in each column of each block is highlighted.

Setting Model Time Gradient removed Residual sky Target signal kept
tiles=16 MEMBRANE 1.1 383 ms 79.0% 71.1 ADU 96.37%
tiles=16 RBF (thin-plate spline) 134 ms 79.0% 71.0 ADU 93.79%
tiles=16 Polynomial 48 ms 65.0% 118.3 ADU 95.06%
tiles=32 MEMBRANE 1.1 411 ms 89.1% 36.9 ADU 94.88%
tiles=32 RBF (thin-plate spline) 243 ms 79.3% 70.1 ADU 94.02%
tiles=32 Polynomial 88 ms 65.7% 115.9 ADU 95.30%
tiles=64 MEMBRANE 1.1 647 ms 91.1% 30.0 ADU 93.88%
tiles=64 RBF (thin-plate spline) 1.63 s 79.1% 70.8 ADU 94.19%
tiles=64 Polynomial 282 ms 65.3% 117.2 ADU 95.45%
tiles=96 MEMBRANE 1.1 641 ms 93.1% 23.2 ADU 92.91%
tiles=96 RBF (thin-plate spline) 5.30 s 88.1% 40.4 ADU 93.27%
tiles=96 Polynomial 219 ms 65.7% 116.0 ADU 95.24%
tolerance=1.0 MEMBRANE 1.1 500 ms 87.0% 43.9 ADU 96.74%
tolerance=1.0 RBF (thin-plate spline) 235 ms 77.7% 75.5 ADU 95.37%
tolerance=1.0 Polynomial 89 ms 64.5% 120.1 ADU 96.09%
tolerance=1.5 MEMBRANE 1.1 499 ms 89.1% 36.9 ADU 94.88%
tolerance=1.5 RBF (thin-plate spline) 249 ms 79.3% 70.1 ADU 94.02%
tolerance=1.5 Polynomial 90 ms 65.7% 115.9 ADU 95.30%
tolerance=2.5 MEMBRANE 1.1 494 ms 90.1% 33.6 ADU 92.00%
tolerance=2.5 RBF (thin-plate spline) 252 ms 80.1% 67.1 ADU 91.95%
tolerance=2.5 Polynomial 88 ms 63.7% 122.9 ADU 94.45%
smoothing=0.0001 MEMBRANE 1.1 423 ms 91.1% 30.0 ADU 94.02%
smoothing=0.0001 RBF (thin-plate spline) 246 ms 85.2% 49.9 ADU 92.60%
smoothing=0.001 MEMBRANE 1.1 501 ms 89.1% 36.9 ADU 94.88%
smoothing=0.001 RBF (thin-plate spline) 244 ms 79.3% 70.1 ADU 94.02%
smoothing=0.01 MEMBRANE 1.1 497 ms 79.9% 68.1 ADU 96.92%
smoothing=0.01 RBF (thin-plate spline) 246 ms 63.7% 122.7 ADU 96.71%
smoothing=0.05 MEMBRANE 1.1 498 ms 60.6% 133.2 ADU 99.03%
smoothing=0.05 RBF (thin-plate spline) 246 ms 30.6% 234.6 ADU 99.21%
degree=1 Polynomial 83 ms -0.3% 339.0 ADU 100.88%
degree=2 Polynomial 89 ms 65.7% 115.9 ADU 95.30%
degree=3 Polynomial 99 ms 65.8% 115.7 ADU 95.12%
degree=4 Polynomial 111 ms 76.8% 78.3 ADU 93.68%
degree=5 Polynomial 123 ms 77.7% 75.4 ADU 93.15%
degree=6 Polynomial 143 ms 80.2% 67.0 ADU 93.12%
rigidity=0.0 MEMBRANE 1.1 268 ms 88.0% 40.4 ADU 94.94%
rigidity=0.25 MEMBRANE 1.1 454 ms 89.1% 36.9 ADU 94.88%
rigidity=0.5 MEMBRANE 1.1 497 ms 89.1% 36.7 ADU 94.83%
rigidity=1.0 MEMBRANE 1.1 500 ms 88.9% 37.5 ADU 94.78%
protection=0 MEMBRANE 1.1 291 ms 89.6% 35.2 ADU 94.13%
protection=1 MEMBRANE 1.1 497 ms 89.1% 36.9 ADU 94.88%
protection=2 MEMBRANE 1.1 675 ms 88.7% 38.2 ADU 95.13%
protection=3 MEMBRANE 1.1 837 ms 88.5% 38.9 ADU 95.23%
tiles=16 MEMBRANE 1.1 634 ms 88.5% 18.7 ppm 100.13%
tiles=16 RBF (thin-plate spline) 481 ms 87.9% 19.7 ppm 100.16%
tiles=16 Polynomial 130 ms 84.4% 25.4 ppm 100.17%
tiles=32 MEMBRANE 1.1 721 ms 93.4% 10.7 ppm 100.07%
tiles=32 RBF (thin-plate spline) 890 ms 88.1% 19.4 ppm 100.17%
tiles=32 Polynomial 195 ms 84.6% 25.2 ppm 100.17%
tiles=64 MEMBRANE 1.1 858 ms 93.2% 11.1 ppm 99.91%
tiles=64 RBF (thin-plate spline) 6.53 s 90.4% 15.6 ppm 100.11%
tiles=64 Polynomial 304 ms 84.5% 25.3 ppm 100.17%
tiles=96 MEMBRANE 1.1 1.46 s 93.8% 10.1 ppm 99.84%
tiles=96 RBF (thin-plate spline) 7.15 s 91.2% 14.3 ppm 100.05%
tiles=96 Polynomial 939 ms 84.5% 25.3 ppm 100.17%
tolerance=1.0 MEMBRANE 1.1 698 ms 93.4% 10.7 ppm 100.08%
tolerance=1.0 RBF (thin-plate spline) 880 ms 88.1% 19.4 ppm 100.17%
tolerance=1.0 Polynomial 187 ms 84.6% 25.1 ppm 100.17%
tolerance=1.5 MEMBRANE 1.1 703 ms 93.4% 10.7 ppm 100.07%
tolerance=1.5 RBF (thin-plate spline) 896 ms 88.1% 19.4 ppm 100.17%
tolerance=1.5 Polynomial 199 ms 84.6% 25.2 ppm 100.17%
tolerance=2.5 MEMBRANE 1.1 695 ms 93.4% 10.7 ppm 100.06%
tolerance=2.5 RBF (thin-plate spline) 888 ms 88.2% 19.3 ppm 100.17%
tolerance=2.5 Polynomial 188 ms 84.6% 25.1 ppm 100.17%
smoothing=0.0001 MEMBRANE 1.1 710 ms 94.3% 9.3 ppm 99.99%
smoothing=0.0001 RBF (thin-plate spline) 903 ms 90.1% 16.2 ppm 100.14%
smoothing=0.001 MEMBRANE 1.1 713 ms 93.4% 10.7 ppm 100.07%
smoothing=0.001 RBF (thin-plate spline) 899 ms 88.1% 19.4 ppm 100.17%
smoothing=0.01 MEMBRANE 1.1 701 ms 90.5% 15.4 ppm 100.15%
smoothing=0.01 RBF (thin-plate spline) 888 ms 85.4% 23.7 ppm 100.18%
smoothing=0.05 MEMBRANE 1.1 702 ms 85.3% 23.9 ppm 100.16%
smoothing=0.05 RBF (thin-plate spline) 884 ms 81.1% 30.9 ppm 100.18%
degree=1 Polynomial 182 ms 73.0% 44.1 ppm 100.19%
degree=2 Polynomial 191 ms 84.6% 25.2 ppm 100.17%
degree=3 Polynomial 225 ms 85.4% 23.8 ppm 100.17%
degree=4 Polynomial 266 ms 88.1% 19.4 ppm 100.18%
degree=5 Polynomial 313 ms 88.5% 18.8 ppm 100.18%
degree=6 Polynomial 421 ms 88.7% 18.4 ppm 100.17%
rigidity=0.0 MEMBRANE 1.1 565 ms 92.9% 11.6 ppm 100.08%
rigidity=0.25 MEMBRANE 1.1 695 ms 93.4% 10.7 ppm 100.07%
rigidity=0.5 MEMBRANE 1.1 702 ms 93.5% 10.6 ppm 100.07%
rigidity=1.0 MEMBRANE 1.1 701 ms 93.3% 10.9 ppm 100.07%
protection=0 MEMBRANE 1.1 604 ms 93.5% 10.6 ppm 100.00%
protection=1 MEMBRANE 1.1 709 ms 93.4% 10.7 ppm 100.07%
protection=2 MEMBRANE 1.1 894 ms 93.4% 10.7 ppm 100.08%
protection=3 MEMBRANE 1.1 951 ms 93.4% 10.7 ppm 100.08%

Method, so you can check it: the residual is the standard deviation of the star-rejected sky samples of the corrected frame, averaged over the three RGB channels (M57) or the four Bayer sub-channels (M8). "Gradient removed" compares that against the uncorrected frame. Signal kept is aperture flux for M57 and nebula-minus-sky contrast for M8, so the two frames are not directly comparable on that column. Times are the fastest of two runs of the model fit over every plane.

macOS-26.5.2-arm64-arm-64bit-Mach-O · numpy 2.5.1 · M57 3008×3008×3, uncorrected 163 ppm, measurement floor 7.2 ppm · M8 2180×3856 CFA, uncorrected 338 ADU, measurement floor 21 ADU

That floor matters: a tile median carries its own uncertainty, so the residual can never reach zero. On M57, MEMBRANE at its default settings already sits at the floor, which means no gradient is measurable any more and the remaining percentage points are not reachable by any model.

Where it is not the answer

Three models ship, because three are needed.

If your gradient really is a smooth ramp or a clean vignette, a polynomial is not just adequate, it is more accurate and three times faster, because that gradient is literally a polynomial. MEMBRANE earns its place on the frames that are not: amp glow, moon glow from one edge, asymmetric city light, several of them at once. The polynomial and RBF models are in the same panel, one dropdown away, and the log tells you what each one did.

Trade-off

Flexibility has a price

A model free enough to follow your sky is also free enough to follow faint nebulosity. On a target that fills the frame, expect to give up a few percent of the outer signal. Raise the smoothing, or tighten the rejection tolerance to 1.0, and you keep most of it back.

Limit

It cannot invent samples

When a bright object covers most of the field, every model is guessing in the same places, and no background extractor of any kind will save a frame with no sky left in it.

Honesty

Standing on old work

Weighted membrane interpolation and cascadic multigrid solvers are established numerical methods. What is new here is putting them together for sky background extraction, and tuning the result against real frames rather than synthetic ones.

Get MEMBRANE

A single Python file. Drop it into Siril's script folder and it appears in the Scripts menu, with the polynomial and RBF models included for comparison.

membrane-siril.py · v1.1.0 · numpy + scipy
Download for Siril

Installing

  1. Put the file in Siril's script folder Preferences → Scripts shows the folders Siril watches. Any of them works.
  2. Open a frame Linear data gives the best result: stacked or single, mono, colour or undebayered CFA.
  3. Scripts → Python Scripts → membrane-siril The panel opens. Press Remove Gradient. The open image updates in place, and Siril's undo still works.

Settings worth knowing

model      membrane | rbf | poly
tiles      32     sky samples, long axis
tolerance  1.5    lower rejects more objects
smoothing  0.001  higher = stiffer surface

Defaults suit a sparse field. For a target that fills the frame, such as M8, M42 or the Heart Nebula, set tolerance to 1.0 and smoothing to 0.01: on the M8 sub above that keeps 97% of the nebula instead of 91%, and still removes 40% of the gradient.

Questions

Before you ask

Does it replace DBE or GraXpert?
It does the same job inside Siril, without leaving for another application and without placing sample points by hand. It is not an AI model: nothing was trained, and the same input always produces the same output.
Linear or stretched data?
Linear, before stretching. It runs on stretched images too, but a gradient is only additive while the data is linear, so the correction after stretching is an approximation.
Will it damage my colour balance?
Each channel, or each Bayer sub-channel, is modelled independently and its own sky level is added back. Measured drift between the four CFA channels on the M8 frame was under 0.5%.
Does it add noise?
No. The model is smooth by construction and is only subtracted, so pixel-level noise is unchanged. On the M57 stack it measured identical before and after.