Port of R-based AstroSharp tool to Rust
  • Rust 71.2%
  • Python 20.6%
  • R 4.2%
  • Shell 1.2%
  • Makefile 1%
  • Other 1.8%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-22 18:44:35 -04:00
.cargo Package AstroSharp for Windows on ARM as an Inno Setup installer; bump version to 0.4.4 2026-10-18 19:17:02 -04:00
assets Package AstroSharp for Windows on ARM as an Inno Setup installer; bump version to 0.4.4 2026-10-18 19:17:02 -04:00
crates Add --progress to the CLI; bump version to 0.5.1 2026-10-22 17:26:10 -04:00
siril Siril script: find the CLI on Windows and Linux too (v0.4.1) 2026-10-22 18:01:37 -04:00
testdata/samples Initial Commit 2026-10-11 14:36:44 -04:00
tools Add the Dual PSF and Star Mask models from upstream's DualPSF branch 2026-10-20 19:07:54 -04:00
.gitignore Add FITS read/write with header preservation; bump version to 0.3.0 2026-10-11 18:42:18 -04:00
Cargo.lock Add --progress to the CLI; bump version to 0.5.1 2026-10-22 17:26:10 -04:00
Cargo.toml Add --progress to the CLI; bump version to 0.5.1 2026-10-22 17:26:10 -04:00
LICENSE License under MIT and credit the original author 2026-10-20 14:58:38 -04:00
Makefile Hook the Siril script into the workspace 2026-10-22 18:00:27 -04:00
README.md Hook the Siril script into the workspace 2026-10-22 18:00:27 -04:00

astrosharp-rs

A Rust port of AstroSharp's image processing. The original is an R/Shiny app packaged with Electron. This port covers its models:

  • PSF (sharpen), AstroClean (denoise) and Hybrid (PSF then AstroClean).
  • Dual PSF, from the app's DualPSF branch: one PSF model for the stars and another for the rest of the image (the deep-sky object), mixed through a feathered star mask.
  • Star Mask, which outputs that mask, for tuning it or for use elsewhere.

The legacy "First Beta" and "Second Beta" models are not ported, nor is the DualPSF branch's experimental linear-data PSF, which that app doesn't offer. It reads TIFF and FITS (Siril) images and saves TIFF, FITS, PNG or JPEG.

The trained neuralnet weights are exported from the original .RDS files and embedded in the binaries, so nothing else is needed at runtime: no R, no Electron.

AstroSharp was originally created by Mark Lowry (Deep Sky Detail), who kindly agreed to this open-source port. If you find it useful, consider buying him a coffee.

Downloads

Installers are on the releases page. They aren't signed with a paid certificate, so expect a warning the first time.

  • macOS (Apple Silicon, macOS 11 or later): unzip AstroSharp-<version>-macos-arm64.zip and move AstroSharp.app to Applications.
    • The first time you open it, macOS says it can't check the app for malware. Go to System Settings > Privacy & Security and click Open Anyway; this is only needed once.
    • The CLI is inside the app, at AstroSharp.app/Contents/MacOS/astrosharp-cli.
  • Windows 10 and 11 (x64 or ARM64): run AstroSharp-<version>-setup.exe; see Windows for what it installs.
    • SmartScreen may show "Windows protected your PC". Click More info, then Run anyway.
  • Linux: build and install from source with make install-linux; see Linux.
  • Siril: AstroSharp.py runs AstroSharp from inside Siril; see Siril script.

AstroSharp is meant as a final touch-up on a stretched (non-linear) image. The models were trained on stretched data, so stretch first (in Siril, for example) and run AstroSharp last.

Images that look linear trigger a note in the GUI and on the CLI's stderr: the warning fires when 99% of pixels are darker than 0.15. Linear stacks typically sit at or below 0.04, and stretched images at 0.5 or above. Processing still runs, and linear images also look nearly black in the GUI.

Layout

Path Contents
crates/core Library: MLP inference, 9x9 feature windows, pipeline, star mask (ports of CImg's recursive blur, Hessian and labelling), sRGB↔Luv, TIFF/FITS I/O, plate-solution (WCS) coordinates, embedded weights
crates/cli astrosharp command-line tool
crates/gui astrosharp-gui desktop app (egui/eframe)
crates/telemetry Tracing setup shared by the CLI and GUI: stderr logging and OpenTelemetry export
tools/export_weights.R Converts the .RDS models to crates/core/weights/*.bin
tools/make_golden.R Runs the original R functions headless to produce reference outputs
tools/bundle_macos.sh Builds an unsigned AstroSharp.app
tools/package_windows.ps1, tools/windows/ Builds the Windows (ARM64) installer with Inno Setup
tools/icon Standalone helper (uses resvg) that renders the icon SVG for make icon
assets/ App icon: icon.svg (source), plus the generated AppIcon.icns (macOS), AppIcon.ico (Windows) and icon-512.png (window/Dock icon)
siril/ Siril script (AstroSharp.py) that runs the CLI on Siril's open image, with its tests
testdata/samples The sample TIFFs shipped with the R app

Usage

make lists the common tasks:

make run [IMAGE=image.tif]   # desktop app
make release                 # build target/release/astrosharp and astrosharp-gui
make app                     # target/release/bundle/AstroSharp.app (unsigned)
make install                 # ...and copy it to /Applications (APP_DIR=... to change)
make install-linux           # Linux: install the GUI, CLI and a desktop launcher in ~/.local
make check                   # rustfmt check + clippy + tests
make icon                    # re-render assets/icon.svg into AppIcon.icns, AppIcon.ico + icon-512.png (commit the results)
make golden                  # regenerate R reference outputs (R_APP=... if not ../AstroSharp/resources/app)
make siril-install           # copy the Siril script to ~/.siril/scripts
make siril-test              # test the Siril script against a fresh CLI build (needs Python 3)

The app bundle also contains the CLI, at /Applications/AstroSharp.app/Contents/MacOS/astrosharp-cli after make install (the Siril script uses it from there).

CLI example: astrosharp in.fit out.fit --model hybrid --psf 3 --aggressiveness 0.66. The input can be TIFF or FITS (.fit/.fits/.fts). Options:

  • --model dual-psf|hybrid|psf|astroclean|star-mask (default dual-psf)
  • --psf <1..8 step 0.25>: the PSF model; for dual-psf, the one used away from stars
  • --star-psf <1..8 step 0.25>: the PSF model for the stars (dual-psf)
  • --aggressiveness <0..1>
  • --mode seamless|parity (see Modes)
  • --chunk-size <n> (Hybrid and Dual PSF in parity mode only)
  • Star mask (dual-psf, star-mask), with the R app's defaults:
    • --sensitivity <0..100> (98): percentile of the star response above which pixels count as stars.
    • --fix-mask <n> (2): leaves out the regions numbered up to n, counted down each column from the left. Region 0 is normally the background.
    • --feathering <sigma> (5): blur of the mask's edges, in pixels.
    • --blend <a> (9) and --blend2 <b> (100): shape of the beta curve that turns the feathered mask into weights.
  • Output format follows the file extension: .tif/.tiff, .fit/.fits/.fts, .png or .jpg/.jpeg
  • --depth <8|16|32>:
    • TIFF: 8, 16 (default) or 32 (float).
    • FITS: 16 or 32. The default is the input FITS's depth, otherwise 32.
    • PNG: 8 or 16 (default).
    • JPEG: 8 only.
  • --quality <1..100> (JPEG only, default 90)
  • --progress: print progress to stdout while processing, as progress <fraction> lines from 0 to 1, for scripts such as the Siril one

GUI (Dual PSF is the default model):

  • Processing starts with "Process image"; opening a file never processes it. A progress bar with Cancel shows while it runs.

    • The buttons are colour-coded by what they do: blue processes, amber discards the result, red discards the result and the settings.
  • Live update (off by default) reprocesses about 0.25 s after the last settings change. With it off, nothing is processed until you press a button.

    • Changing only Aggressiveness reuses the last run's neural-network results, so the update takes a fraction of a second (about 0.1 s at 20 MP, compared with 1.7 s for a full Hybrid run). The result is identical to processing from scratch.
    • With Dual PSF and Star Mask, the star mask settings (and for Dual PSF the mode and chunk size) don't need the networks rerun either. The image's star response, the costly part of the mask, is also kept, so a mask change takes about 0.3 s at 19 MP, compared with 2.2 s for a full Dual PSF run.
    • A change made while the first run is still going waits for it to finish, rather than starting over.
    • The reused results take memory, so they're only kept when the original, the result and the results fit in half the installed RAM. For example, on an 8 GB machine an 89 MP colour image goes without, and every update reprocesses. The star response takes 8 bytes per pixel.
  • Navigation: drag to pan, scroll or pinch to zoom, double-click to fit. Hold Space to show the original.

  • Cursor readout (bottom left): the pixel under the cursor, counted from the top-left like Siril, and the zoom.

    • For a plate-solved FITS file it also shows the sky coordinates, e.g. α 23h59m47s δ +66°42'41", matching Siril's readout. The status line says "plate-solved" for such files.
    • See Plate solutions for which solutions are supported.
  • Dual PSF has two PSF sliders: DSO PSF for the image as a whole and Star PSF for the stars. Star Mask shows the mask that Dual PSF uses; tune it there, then switch to Dual PSF. Both have a Star mask section with the five mask settings (see the CLI options above) and a Defaults button.

  • Histogram (below the save options): the displayed result's histogram, as in the R app. For Dual PSF and Star Mask, the red line is the Blend / Blend 2 beta curve; it maps the feathered mask to weights.

  • Compare before/after (off by default) splits the view into original | processed. Drag the divider's handle (or the line) to move it.

  • Preview region only (off by default) processes just a region while you adjust settings. Choose it with Shift-drag or right-drag, and process the whole image with "Process full image". "Update preview" refreshes the region; with Live update on it also refreshes automatically.

  • Open image… (or drop a file on the window) accepts TIFF and FITS.

  • Reset image and Reset all sit under the processing buttons. Both discard the processed result, cancelling any processing in progress, and reload the image from disk, for example after you've changed it in another program. If the file can no longer be read, the image as first loaded is shown.

    • Reset image keeps the model settings. The zoom and preview region are kept too, unless the image's size changed.
    • Reset all also restores the default model settings (Dual PSF, DSO and star PSF 3, Aggressiveness 0.66, Seamless, and the star mask defaults) and a fresh view.
    • Both keep the view options and the Save as choice. With Live update on, neither reprocesses until you change a setting.
  • Save as picks the output format:

    • TIFF (default): 16-bit, or 32-bit float for further processing.
    • FITS: 32-bit float or 16-bit. This is selected automatically, at the source's depth, when you open a FITS file.
    • PNG: lossless, 16-bit (default) or 8-bit.
    • JPEG: quality 50–100 (default 90), written without chroma subsampling to keep small coloured stars crisp.

    Saving runs in the background.

  • Quit closes the app.

Linux

  • Installing: make install-linux (or tools/install_linux.sh) builds a release and installs AstroSharp for the current user, with no root needed.
    • The GUI goes in as ~/.local/bin/astrosharp-gui and the CLI as ~/.local/bin/astrosharp-cli, the same name as on macOS and Windows.
    • It adds an AstroSharp launcher with the app icon to ~/.local/share, so the app appears in the desktop's app grid and its window gets the right icon in the dock.
    • make uninstall-linux removes it all again. BINDIR and XDG_DATA_HOME change where the files go.
  • Building only: cargo build --release -p astrosharp-gui -p astrosharp-cli builds without installing.
  • Wayland desktops (e.g. GNOME): the GUI runs through XWayland, because that's the only way files dropped from the file manager reach it.
    • ASTROSHARP_WAYLAND=1 runs it as a native Wayland client instead. That's sharper with fractional scaling, but files can't be dropped on it.
    • If the X server isn't reachable, it falls back to Wayland by itself and logs a warning.

Windows

tools/package_windows.ps1 builds one installer for both 64-bit Windows architectures, ARM64 and x64; it installs the native build on each machine. Build it on Windows on ARM (for example Windows 11 under Parallels on Apple Silicon), where the ARM64 toolchain also cross-compiles the x64 build. Run it from the repository in PowerShell:

powershell -ExecutionPolicy Bypass -File tools\package_windows.ps1
  • Output: target\release\bundle\windows\AstroSharp-<version>-setup.exe.
  • Prerequisites:
    • Rust's native ARM64 toolchain, with the x64 target added: rustup toolchain install stable-aarch64-pc-windows-msvc, then rustup target add x86_64-pc-windows-msvc --toolchain stable-aarch64-pc-windows-msvc. It needn't be your default; an x64 default runs under emulation and is noticeably slower.
    • Visual Studio 2022 Build Tools with MSVC v143 – VS 2022 C++ ARM64/ARM64EC build tools, MSVC v143 – VS 2022 C++ x64/x86 build tools and a Windows SDK.
    • Inno Setup 6: winget install --id JRSoftware.InnoSetup -e.
  • The installer:
    • Installs for the current user only, with no administrator rights needed, into %LOCALAPPDATA%\Programs\AstroSharp.
    • Picks the build that matches the machine: ARM64 on Windows on ARM, x64 on Intel and AMD PCs.
    • Installs AstroSharp.exe (the GUI) and astrosharp-cli.exe (the CLI), adds a Start menu shortcut, and registers an uninstaller in Settings > Apps.
    • Offers two unticked options: a desktop shortcut, and adding the CLI to your PATH.
  • The executables carry the AstroSharp icon and version details, need no Visual C++ Redistributable, and the GUI opens without a console window.
  • Signing: the installer isn't code-signed. Windows SmartScreen only warns about it when it was downloaded from the internet.

Siril script

siril/AstroSharp.py runs AstroSharp on the image open in Siril (1.4 or later):

  • Every model, with the same settings as the app.
  • A preview with zoom, pan and a before/after divider, and a progress bar while it processes.
  • Send to Siril applies the result with an undo point and records the settings in the FITS HISTORY.
  • It also runs from Siril scripts (.ssf), for example pyscript AstroSharp.py -model dual-psf -starpsf 2.

To install it, put AstroSharp.py from a release in a folder on Siril's script path (listed under Preferences → Scripts); from a checkout, make siril-install copies it to ~/.siril/scripts. It uses the AstroSharp CLI from the app's install location. siril/README.md has the details.

FITS (Siril)

  • Supported files: uncompressed FITS with the image in the primary HDU, grey or RGB (three planes). Samples can be 8/16/32/64-bit integer or 32/64-bit float, which covers what Siril writes.

  • Not supported: compressed FITS (.fz) and images stored only in extensions.

  • Values: integer data is scaled by its type's range (after BZERO/BSCALE). Float data is used as stored, since Siril keeps it in 0–1; float data clearly outside that range is scaled down and restored on save. Blank (NaN) pixels become 0.

  • Row order: follows ROWORDER, defaulting to FITS's bottom-up. Images are processed top row first, like TIFFs, and written back in the source's row order.

  • Header: saving as FITS from a FITS source keeps every header card verbatim and in order, except for the following:

    • Structural keywords (SIMPLE, BITPIX, NAXISn, EXTEND, BZERO, BSCALE) are regenerated.
    • Values made stale by processing (CHECKSUM, DATASUM, DATAMIN, DATAMAX, BLANK) are dropped.
    • DATE is refreshed.
    • A HISTORY AstroSharp <version>: <settings> line is appended.

    Plate solutions (WCS/SIP), observation details and history are therefore kept, and any HDUs after the image are copied unchanged.

  • Plate solutions: the GUI reads the WCS keywords that Siril, ASTAP or astrometry.net write.

    • Supported: the TAN projection, with or without SIP distortion, and a CD, PC + CDELT or CROTA2 + CDELT matrix.
    • Coordinates are in the solution's own frame, normally J2000. Images without a usable solution, including TIFFs and unsolved stacks with only the pointing RA/DEC, show pixel coordinates only.
  • Linear data: see Recommended workflow.

Tracing (OpenTelemetry)

The CLI and GUI can send traces of what they do to an OpenTelemetry endpoint such as VictoriaTraces. Nothing is sent unless an endpoint is configured.

  • Configuration: set the standard variables in the environment, or in ~/.config/astrosharp/otel.env (under $XDG_CONFIG_HOME if that's set; %APPDATA%\astrosharp\otel.env on Windows).

    • Apps launched from Finder don't see shell variables, so use the file for AstroSharp.app, and for the bundled CLI when Siril runs it.
    • An endpoint in the environment overrides the file's.
    # ~/.config/astrosharp/otel.env (KEY=value lines; # starts a comment)
    OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:10428/insert/opentelemetry
    
  • Settings:

    • OTEL_EXPORTER_OTLP_ENDPOINT is a base URL; /v1/traces is appended. OTEL_EXPORTER_OTLP_TRACES_ENDPOINT is a full URL, used as is.
    • OTEL_EXPORTER_OTLP_HEADERS (key=value,key=value) and OTEL_EXPORTER_OTLP_TIMEOUT (milliseconds, default 5000).
    • OTEL_SDK_DISABLED=true turns export off.
    • OTEL_SERVICE_NAME and OTEL_RESOURCE_ATTRIBUTES work from the environment only.
  • Transport: OTLP over plain http://, protobuf-encoded. HTTPS and gRPC aren't built in.

  • Services: astrosharp-cli and astrosharp-gui, tagged with service.version.

  • Spans cover each operation and processing stage:

    • The CLI's run span has read_image, process and save_image as children.
    • In the GUI, open_image, process_job (full image or preview, with an outcome of ok, cancelled or failed), save_job and reset (image or all) each start their own trace.
    • Under process: to_luv and from_luv (colour only), psf_pass and astroclean_pass, or hybrid_parity in R parity mode.
    • For Dual PSF and Star Mask: star_response, star_mask (with the threshold and number of regions), psf_pass with which = dso or stars, and dual_parity_masks in R parity mode.
    • Previews have a region field on process.
    • Failures mark the top span as an error, with the message.
  • Detail: ASTROSHARP_TRACE filters what is exported, using EnvFilter syntax (default astrosharp=info).

    • ASTROSHARP_TRACE=astrosharp=debug adds a span per parity-mode chunk and FITS header details.
    • It can also go in otel.env.
  • Logging: warnings and errors, including failed exports, go to stderr; RUST_LOG changes the level (default warn).

  • Privacy: traces include the full paths of the files you open and save.

Fidelity to the R app

cargo test --release checks the port against reference outputs generated by the R code:

Rscript tools/make_golden.R ../AstroSharp/resources/app testdata/golden   # once, ~50 s
cargo test --release

The reference app is the DualPSF branch of AstroSharp; the Dual PSF and Star Mask references need the imager (tested with 1.0.8), magrittr and dplyr packages. If testdata/golden is missing, the R-comparison tests print SKIPPED and pass. The tests cover:

  • Processed values: PSF, AstroClean and Hybrid match R within 2.4e-15 (f64 rounding noise), and Dual PSF and Star Mask within 2e-13. The cases cover grey and colour images, several chunk sizes, preview crops and non-default mask settings.
  • Star mask: the ports of CImg's blur and Hessian match a plain-arithmetic transcription in R bit for bit, and imager itself within 6e-13. imager may be compiled with fused multiply-adds, which explains the difference. Thresholds, region labels and the thresholded pixels are identical. pbeta (a port of the Cephes incomplete beta) matches R within 3e-13.
  • Colour output: matches R exactly.
  • 16-bit TIFFs: identical to the R app's writeTIFF output, sample for sample.
  • Building blocks: readTIFF scaling, R's round(x, 5) and the sRGB↔Luv conversion are bit-identical.

Behaviours deliberately reproduced:

  • Unprocessed border. Only pixels 5..n-5 (1-based) are processed; a 4 px border at the start and 5 px at the end keep their input values.
  • Feature order. The 9x9 features use getmatrix9's irregular column order, which the models were trained on.
  • Colour. Colour images go through R's sRGB↔Luv (R's own D65 white point), only L is processed, and the output is rounded to 5 decimals.

Modes

  • parity reproduces the R app exactly:

    • Hybrid is processed in chunks, including faint seams at chunk boundaries: stage 2 sees unsharpened pixels at each chunk's rim.
    • Dual PSF builds a separate star mask for each chunk, so its result depends on the chunk size.
    • Star Mask of a colour image is grey RGB.
  • seamless (default) processes the whole image at once instead:

    • Hybrid runs in two whole-image passes.
    • Dual PSF uses one star mask for the whole image, the one Star Mask shows.
    • Colour output keeps full precision. R rounds it to 5 decimals, which would cost precision in 32-bit linear data.
    • Star Mask outputs a single grey channel.

    For grey PSF, AstroClean and Star Mask, both modes give identical output.

Intentional differences from the R app:

  • Colour vs grey is detected from the file. The R app needed the correct radio button and failed otherwise.
  • Preview uses any region you choose and gives exactly the full-image result there (except Hybrid in parity mode). Models that look at nearby pixels process the region with an 8 px margin; the star mask comes from the whole image. The R app processed a fixed 10% crop as a standalone image.
  • Histogram bins are a fixed 1000 over 0–1; R's hist picks its breaks from the data.
  • Star mask in the R app's Windows build comes from an older imager release, which may treat Feathering below 0.5 differently; the references come from imager 1.0.8.
  • Out-of-range values saturate when written to 16-bit; R's writeTIFF wraps them.
  • Alpha channels are dropped instead of failing.
  • TIFF tags. Output files carry a Predictor=1 tag. libtiff in R logs a harmless "unknown field 317" warning when reading them.

Performance

Apple M5 Max, 6000x4000 (24 MP) image:

R app (single core) Rust
PSF, grey 110 s 0.95 s
Hybrid, grey not run at 24 MP (34.5 s at 4 MP) 2.0 s (2.9 s parity; 0.32 s at 4 MP)
PSF / Hybrid, RGB — 1.2 s / 2.2 s
Dual PSF, grey / RGB — 2.5 s / 2.7 s (same in parity mode)
Star Mask, grey / RGB — 0.45 s / 0.5 s

Memory: images are held in double precision, which is 8 bytes per channel per pixel. An 89 MP colour image therefore takes 2.1 GB.

  • CLI: peaks at about 2.3 times that, roughly 5 GB.
  • GUI: also holds the result. While reprocessing, it frees the old result's full-resolution copy and keeps only what's on screen.

License

MIT; see LICENSE. The original AstroSharp, including the trained models embedded here, is by Mark Lowry (Deep Sky Detail) and is also MIT-licensed.