EigenFrame.PreProcessing
0.4.25
See the version list below for details.
dotnet add package EigenFrame.PreProcessing --version 0.4.25
NuGet\Install-Package EigenFrame.PreProcessing -Version 0.4.25
<PackageReference Include="EigenFrame.PreProcessing" Version="0.4.25" />
<PackageVersion Include="EigenFrame.PreProcessing" Version="0.4.25" />
<PackageReference Include="EigenFrame.PreProcessing" />
paket add EigenFrame.PreProcessing --version 0.4.25
#r "nuget: EigenFrame.PreProcessing, 0.4.25"
#:package EigenFrame.PreProcessing@0.4.25
#addin nuget:?package=EigenFrame.PreProcessing&version=0.4.25
#tool nuget:?package=EigenFrame.PreProcessing&version=0.4.25
EigenFrame.PreProcessing
Pure, dependency-free astrophotography preprocessing algorithms for .NET — frame classification, sample decode, tile-pyramid generation, image statistics, tile-based calibration, and stacking/integration math. Written in F#, usable from any .NET language.
The library is format-agnostic: it operates on already-decoded sample buffers and metadata, never on files. You read your XISF/FITS bytes however you like and hand the library canonical buffers; it does the math. This is what lets the same algorithms run identically in a cloud service and a local desktop app.
dotnet add package EigenFrame.PreProcessing
<PackageReference Include="EigenFrame.PreProcessing" Version="0.2.0" />
Targets .NET 10. Floors FSharp.Core at 10.0.100 (deliberately low — any net10
SDK satisfies it). MIT licensed.
The one thing to know: the canonical contract
Every pixel function works in one processing format: single-channel / planar
float32 (or float), range [0, 65535] — not [0, 1]. You convert your source
samples into that range; the library never guesses. The per-source-format scaling is
provided for you via SampleFormat:
type SampleFormat = UInt8 | UInt16 | UInt32 | Float32 | Float64
| Source | → canonical [0, 65535] |
|---|---|
UInt8 |
v / 255 * 65535 |
UInt16 |
identity (raw ADU) — the zero-cost path |
UInt32 |
v / UInt32.MaxValue * 65535 |
Float32 |
v * 65535 (XISF [0,1] → [0,65535]) |
Float64 |
v * 65535 |
SampleFormat is the library's own enum on purpose — it does not reference
XisfLib.Core or any image-format library. If your data comes from XISF, map its
XisfSampleFormat to SampleFormat once at your boundary (a ~5-line match).
A few invariants worth honoring:
- Pure & synchronous. No I/O, no
async— you own all reads/writes and thread the compute off your request threads. - You own the buffers. In-place variants (e.g.
subtractBiasInPlace,normalizeInPlace) mutate the slice you pass and are named accordingly. (Some functions still allocate-and-return while the API settles pre-1.0.) - Tile geometry is library-canonical. Tile sizes, pyramid structure, and the
[0,1]tile wire format are decided by the library so independently-built tiles line up — don't reinvent them host-side.
Pre-1.0: the surface may change between minor versions until 1.0. Pin a version.
Modules
| Module | What it does |
|---|---|
Classification |
Bias/Dark/Flat/Light × Frame/Master from frame metadata |
Decode |
raw bytes → canonical [0,65535] samples; level dimensions; area-average downsampling |
Statistics |
min/max/mean/median/stddev/MAD (exact UInt16 histogram path) |
Tiling |
tile-grid geometry, [0,1] byte encoders, Brotli, per-level tile generation, meta.json |
Calibration |
scaled-out tile-based bias subtraction |
Integration |
tile decode, normalization, pixel rejection (MinMax / SigmaClip / LinearFitClip), combine |
Stars, Alignment |
planned |
Examples
Classify a frame from its FITS signals — the IMAGETYP value (or None if absent)
and whether an NCOMBINE keyword is present (the master marker):
open EigenFrame.PreProcessing
match Classification.classify (Some "Light Frame") false with
| Classification.Classified (cat, sub) -> printfn "%A %A" cat sub // Frame Light
| Classification.Unknown -> printfn "unrecognised"
// Canonical persisted names, e.g. for a catalog row:
let category, subtype = Classification.names (Classification.classify (Some "Master Dark") true)
// "Master", "Dark"
Decode + measure a frame you've already read into a ReadOnlyMemory<byte>:
open EigenFrame.PreProcessing
let fmt = SampleFormat.UInt16 // map from your source format
let stats = Statistics.computeStats pixelBytes pixelCount fmt
printfn "mean=%f median=%f" stats.Mean stats.Median // both in [0, 65535]
Build the L4 tile pyramid straight from source bytes:
let tileSize = Tiling.validateTileSize 512
for (tx, ty, brotliBytes) in Tiling.generateL4Tiles pixelBytes width height fmt tileSize do
// write brotliBytes to your tile store at (tx, ty)
()
License
MIT.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- FSharp.Core (>= 10.0.100)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.4.38 | 107 | 6/24/2026 |
| 0.4.37 | 76 | 6/24/2026 |
| 0.4.36 | 76 | 6/24/2026 |
| 0.4.35 | 99 | 6/24/2026 |
| 0.4.34 | 101 | 6/23/2026 |
| 0.4.33 | 96 | 6/23/2026 |
| 0.4.32 | 101 | 6/23/2026 |
| 0.4.31 | 111 | 6/23/2026 |
| 0.4.30 | 118 | 6/22/2026 |
| 0.4.29 | 95 | 6/22/2026 |
| 0.4.28 | 97 | 6/21/2026 |
| 0.4.27 | 110 | 6/20/2026 |
| 0.4.26 | 101 | 6/20/2026 |
| 0.4.25 | 99 | 6/20/2026 |
| 0.4.24 | 111 | 6/20/2026 |
| 0.4.23 | 100 | 6/19/2026 |
| 0.4.22 | 97 | 6/19/2026 |
| 0.4.21 | 96 | 6/19/2026 |
| 0.4.20 | 114 | 6/18/2026 |
| 0.4.19 | 271 | 6/13/2026 |