imcombiners

Image + Combiner + rs (Rust) = imcombiners = imc
imcombiners combines astronomical image stacks with pixel rejection, normalization, masks, and diagnostics. It also accepts stacks with arbitrary trailing dimensions and supports IRAF IMCOMBINE compatibility settings.
The package has four usage modes: Standard Combiner, compact ndcombine() wrapper, Chained Combiner, and direct kernel calls. Start with Standard Combiner for ordinary Python workflows; use direct kernel calls only when you are building a custom high-throughput layer.
Compatibility tests compare supported combinations and rejection settings against IRAF, Astropy/NumPy, and ccdproc reference outputs.
1. First Look
Rust-backed Python kernels combine and reject stacks along axis 0 (see below for reasons to choose axis 0, not -1). Inputs are arrays shaped (N, *spatial), which is the natural result of np.array([image for image in images]) or np.stack(images, axis=0). For ordinary 2-D image of shape (H, W), it is (N, H, W), but arbitrary higher-dimensional arrays are supported. Combination and rejection return the final output shape of (*spatial).
import numpy as np
import imcombiners as imc
rng = np.random.default_rng(20250311)
stack = rng.normal(1000, 5, (15, 256, 256)).astype("float32")
cmb = imc.Combiner(stack)
out = cmb.combine(
"median", # final stack-combination method
# 1. Optional pre-rejection threshold masking
thresholds=(0.0, 65000.0),
# 2. Optional per-image zero/scale normalization
zero=None,
scale="median",
# 3. Optional pixel rejection before final combination
rejectors=[
imc.MinMaxClip(n_min=1, n_max=0.1),
imc.SigClip(sigma=3.0, maxiters=5),
],
diagnostics=None, # output-only fast path
)Use the public API at the level that matches your workflow:
| Usage mode | Use when |
|---|---|
| Standard Combiner | You want the final image, optional zero/scale/rejection, and a cmb object you can still inspect. |
| compact ndcombine() wrapper | You want a compact function call, especially for IRAF-style or CLI-facing code. |
| Chained Combiner | You need retained masks, clipping bounds, iteration counts, or step-by-step diagnostics. |
| direct kernel calls | You have already prepared arrays and want focused imcombine primitives such as imck.sigclip_combine(...) or imck.nanaverage(...). |
Standard Combiner is the recommended path in these tutorials. Chained Combiner does more work because it retains state; direct kernel calls are for callers who know which validation, copying, diagnostics, and object construction they can safely skip.
A convenience function place_into_padded(images, offsets) is also available for integer-pixel offset placement before combining. It accepts arrays with any shared number of trailing dimensions. Offsets are in NumPy axis order ((dy, dx) for 2-D images), and uncovered pixels are filled with NaN by default.
One dtype detail differs by mode: pure compact ndcombine() calls with combine="lmedian" preserve accepted integer dtypes, while Combiner workflows with masking, rejection, zero, scale, or weights use the normal floating workspace.
2. Rust Crate Use
imcombiners is also usable as a Rust crate. The public Rust-facing API exposes lower-level stack combine and rejection kernels.
[dependencies]
imcombiners = "<version>"Replace <version> with the release version you want to depend on.
Build local Rust API docs with:
cargo doc --no-default-features --open3. Features
- Combine methods: mean, median, lower median (
lmed), percentiles, sum, min, max, variance, and weighted mean viaweight=on mean/average. - Rejection methods: sigma clipping, CCD-noise clipping, iterative center-relative linear clipping, min/max clipping, and IRAF-style
pclip. - Optional threshold masking, zero/scale normalization, and integer-pixel offset padding are available for common image-stack workflows.
- Iterative sigma/CCD/linear clipping,
nkeeprollback, min/max counts, and scalarpcliprank-offset behavior follow the supported IRAF compatibility model. - Public input dtypes are intentionally limited; unsupported dtypes such as
int64andfloat128are not silently cast. See Conventions for the exact dtype policy. - Rejection diagnostics use
mask, std, low, upp, nit, output_flags:stdis an array for sigma/CCD clipping andNonefor rejection methods that do not compute a standard-deviation diagnostic.
4. Rejection
| Spec | Kernel | One-liner |
|---|---|---|
SigClip |
iterative | sigma clipping with nkeep / maxrej |
CcdClip |
iterative | sigma clipping with CCD noise-model std |
LinearClip |
one-pass by default; iterative with maxiters > 1 |
center-relative affine retained bounds |
MinMaxClip |
one-pass | reject N lowest + N highest |
PClip |
one-pass | IRAF-style rank-offset percentile clipping |
Iterative sigma, CCD, and linear clipping recompute the clipping center from the current survivors each pass. LinearClip(maxiters=1) runs one pass; set a larger maxiters to iterate. IRAF-like nkeep rollback is controlled with revert_on_nkeep=True, which is the default safety mode; set it to False only for strict clipping that may leave fewer than nkeep samples. Rejection centers accept mean, median/med, and lower median (lmedian/lmed).
5. Conventions
- Stacks are
(N, *spatial). Combination and rejection always run along the stack axis (axis 0) and return(*spatial). - Terminology: the stack axis is axis 0 (
N). An output element is one location in(*spatial), such as one image pixel. Diagnostics shaped(N, *spatial)are per-sample; diagnostics shaped(*spatial)are per-output-element. Documentation avoids using “column” for axis-0 stacks because CCD images already use row/column for spatial axes. - Accepted public input dtypes are
uint8,uint16,int16,int32,float32, andfloat64. Pure stack reductions such asmean,median,sum, andvariancereturnfloat64for integer inputs; exact selectors such asmin,max, andlmedianpreserve integer dtype. Masking, rejection, zero/scale, and combined rejection/reduction use a floating workspace (uint8,uint16, andint16tofloat32;int32tofloat64). Other dtypes are not silently cast. - Stack reductions exclude both
NaNandinf. For example, the mean of[1, inf, 3]is2. - A kernel is a function in
import imcombiners.kernels as imckthat directly uses Rust to compute a result from input arrays. - Variance is available as
Combiner(...).variance(ddof=...)or the aliasescombine="variance"/"var". It is computed from final valid values:np.nanvar(arr_eff, axis=0, ddof=ddof).np.sqrt(var)describes scatter across retained samples, not the uncertainty of the combined image. - Rejection kernels return a 6-tuple
(mask_rej, std, low, upp, nit, output_flags)wherestdis the clipping spread for SigClip and reference noise for CCDClip (Nonefor algorithms without a spread diagnostic).output_flags=0means normal completion and nonzero status bits are a bit-OR of1=pre-masked input,2=maxiters hit,4=nkeep hit,8=maxrej hit, and16=grow added rejected samples. - High-level diagnostics are controlled with
diagnostics:Nonereturns only the combined image,"simple"returns the current per-output-element diagnostics, and"full"appends a stack-shapeduint8sample_flagsarray.sample_flagsuses a separate bit namespace:1=input mask/BPM,2=non-finite,4=threshold mask,8=final algorithm rejection,16=added by grow, and32=already masked before this rejection step. - Spatial offsets use
(dy, dx)in Python/NumPy order. Photometric zero offsets use thezero=correction in combine APIs; keep these concepts separate when porting FITS/WCS workflows.
6. Memory
The implementation works on full arrays. Memory depends on dtype promotion, workspace ownership, normalization, and requested diagnostics. Combiner copies its workspace by default; supported output-only rejection avoids a stack-sized rejection-mask result, while diagnostic paths retain masks and per-pixel products. Measure peak memory for the actual pipeline; fixed multiples of stack.nbytes are not guaranteed. Spatial chunking is not exposed.
7. Performance
This package is built around image-stack reductions where both algorithmic work and memory traffic matter. The fastest high-level rejection path is usually Standard Combiner with diagnostics=None, or the equivalent compact ndcombine() call, because those routes can avoid diagnostic products and use fused rejection-plus-combine kernels where supported.
The detailed performance model lives in How To Get Maximum Performance:
- Implementation Tricks explains the Rust-side choices such as fused output-only kernels, scratch reuse, variance-domain sigma clipping, selective median, and rank-pair final combine.
- API Choice explains when Standard Combiner,
ndcombine(), Chained Combiner, and direct kernel calls differ in requested work. - Workspace Requirements, Why The Stack Axis Is First, and Thread Tuning cover layout and tuning.
Measured image-combination results are in 2-D Image Benchmarks.