Astropornography tool โ one command from raw subs to a finished astrophoto
Project description
aPornTool
One command from raw sub-exposures to a finished, share-ready astrophoto.
๐ฆ Install: pip install aporn-tool โ releases ยท changelog.
aPornTool takes a folder of raw astrophotography subs and drives them through the full linearโnonlinear editing pipeline โ stacking, photometric colour calibration, gradient and noise removal, colour-preserving stretch, and star management โ leaving only the deliverables in your output folder and every scratch file tucked into a hidden working directory.
It's built for the ZWO Seestar workflow (internal calibration โ no darks/flats/bias) but works
with any OSC .fit subs, and runs natively on Windows, macOS (incl. Apple Silicon), and Linux.
Design goal โ processing parity: on the same data, aim to match what a skilled processor would get, so the only remaining variable is capture (aperture, integration hours, sky darkness). The tool can't invent detail that isn't in the data โ the final crop, curves, and watermark are yours to do from the 16-bit TIFF (Canva/Photoshop).
Contents
- How it works
- Prerequisites
- Installation
- First-time setup
- Usage โ from simplest to advanced
- Command reference
- Stages & resume
- Output layout
- Configuration file
- FAQ
- Troubleshooting
- Limitations
- Development
How it works
raw .fit subs โโโบ stage โโโบ SIRIL: calibrate โโบ register โโบ stack โโบ [SPCC] โโบ golden linear stack
โ
deliverables โโโ finish โโโ [StarNet] โโโ [GraXpert BGE + denoise] โโโ [auto-crop] โโ
(.tif/.png/.jpg/.fits)
- Each stage is checkpointed. The golden linear stack (
_work/<target>/02_linear/) is the immutable re-processing anchor โ re-finishing never re-stacks. - The pipeline is parameterised per mode, not one-size-fits-all: registration, gradient tool, SPCC placement, and star handling differ (see the table below).
| Mode | Best for | Registration | Gradient | Stars |
|---|---|---|---|---|
dso-mosaic |
galaxies & multi-panel โ M31, M33, M51, M101, NGC7000 | WCS plate-solve (framing=max) |
GraXpert BGE | StarNet2, blend some back |
dso-emission-nebula |
Hฮฑ HII / SNRs โ M8, M20, M42, M16, Veil | star-based 2-pass | SIRIL subsky |
keep all (rich field) |
dso-reflection-nebula |
blue scattered light โ VdB106, M78 | star-based 2-pass | GraXpert BGE | dual-layer screen blend |
dso-star-cluster |
globulars & open clusters โ M13, M22, M45, M44 | 2-pass (+FWHM cull) | SIRIL subsky |
keep all โ stars are the subject |
Planetary (video โ AutoStakkert โ finish) is planned; it requires a manual GUI stacking step.
Prerequisites
| Tool | Role | Get it |
|---|---|---|
| Python 3.10+ | orchestrator | python.org / your package manager |
Siril 1.4+ (siril-cli) |
stack, register, plate-solve, SPCC, StarNet | https://siril.org/download/ |
| GraXpert 3.x | background extraction + AI denoise (mosaic & reflection) | https://github.com/Steffenhir/GraXpert/releases |
| StarNet2 | star removal | https://www.starnetastro.com/ |
| ffmpeg / ffprobe | planetary + final polish | ffmpeg.org / package manager |
Every DSO tool is natively cross-platform (incl. Apple Silicon). The tool locates each one on
PATH, then in the standard install locations per OS, then from aporntool.config.json โ you
never hard-code paths.
Where the tools usually live per OS
| Windows | macOS | Linux | |
|---|---|---|---|
| siril-cli | C:\Program Files\Siril\bin\siril-cli.exe |
/Applications/Siril.app/Contents/MacOS/siril-cli |
/usr/bin/siril-cli |
| GraXpert | %LOCALAPPDATA%\Programs\GraXpert\GraXpert.exe |
/Applications/GraXpert.app/Contents/MacOS/GraXpert |
package / AppImage |
| StarNet2 | (set in SIRIL) | /usr/local/bin/starnet2 |
/usr/local/bin/starnet2 |
| GraXpert AI models | %LOCALAPPDATA%\GraXpert\GraXpert\{bge,denoise}-ai-models\ |
~/Library/Application Support/GraXpert/{bge,denoise}-ai-models/ |
~/.local/share/GraXpert/โฆ |
| SIRIL config | %LOCALAPPDATA%\siril\ |
~/Library/Application Support/org.siril.Siril/siril/ |
~/.config/siril/ |
Installation
Already have Python 3.10+? Install from PyPI:
pip install aporn-tool
That gives you the aporn-tool command. python -m aporntool also works as a module fallback
(the Python module keeps the one-word name โ module names can't contain hyphens), and an aporntool
alias command is installed too, so either spelling runs.
Or install from source (for development / latest main)
git clone https://github.com/christiantroyandrada/aporn-tool.git
cd aporn-tool
python3 -m venv .venv
.venv/bin/python -m pip install -e .
# Windows: .venv\Scripts\python -m pip install -e .
Installing the Python package does not install Siril/GraXpert/StarNet โ those are separate apps (see Prerequisites). Run
aporn-tool config --checkto confirm they're found.
First-time setup
Run the discovery check โ it prints where each tool was found and writes a starter config:
aporn-tool config --check
Then complete the three one-time setup steps (the tool's preflight checks the ones it can):
- GraXpert AI models (mosaic & reflection) โ open GraXpert once and run Background Extraction and Denoise on any image so it downloads the model files. Preflight verifies they exist before stacking, so a missing model fails in seconds, not after a 30-minute stack.
- StarNet inside SIRIL (mosaic) โ SIRIL's built-in
starnetcommand runs the executable set in SIRIL โ Preferences โ External Programs. Being onPATHis not enough for mosaic mode. (Reflection mode calls the StarNet CLI directly, so it only needs it discoverable.) - Local Gaia catalogs in SIRIL (SPCC colour calibration) โ online VizieR is retired. Install the sky region matching your target: Milky Way for galactic nebulae/low-latitude clusters, Galaxy Season for high-latitude galaxies. The wrong region โ SPCC reports "no stars".
Validate everything without processing:
aporn-tool dso-mosaic --in "/path/to/subs" --out /path/to/out --target M31 --preflight-only
Usage โ from simplest to advanced
1. The simplest run
Point it at a folder of subs and pick the mode โ that's all. The object name and coordinates are read from the subs' FITS header, and the results land in a folder named after the target, right beside your subs:
aporn-tool dso-mosaic --in "/path/to/M31 subs"
Produces M31_final.tif (16-bit โ the real deliverable), .png, .jpg (quick-look), and .fits
next to the input; everything else lives under _work/.
Override either default when you want to: --target for a custom name, --out for a custom
location.
โ ๏ธ The output path must not contain spaces (a SIRIL limitation). The default output inherits the subs' parent folder; if that path has spaces, pass a space-free
--out. The--inpath and sub filenames may contain spaces.
2. Other modes
aporn-tool dso-emission-nebula --in "/data/M8"
aporn-tool dso-reflection-nebula --in "/data/M78"
aporn-tool dso-star-cluster --in "/data/M13"
3. Combine multiple nights (more integration = the #1 quality lever)
--in is repeatable; all .fit from every source are staged and stacked together:
aporn-tool dso-mosaic \
--in "/data/M31/2026-07-04" \
--in "/data/M31/2026-07-05" \
--out /data/out --target M31
4. A target not in the built-in catalog
Nothing special needed โ the coordinates come from your subs' FITS header, so unlisted targets
(VdB 106, Sh2-155, โฆ) just work. Pass --target only when you want a custom output name:
aporn-tool dso-reflection-nebula --in "/data/vdb106"
aporn-tool dso-emission-nebula --in "/data/sh2-155" --target Sh2-155
5. Control the crop
Auto-crop (default) trims empty registration/mosaic borders. Override it:
# keep the full frame (no crop)
aporn-tool dso-mosaic --in "/data/M31" --out /data/out --target M31 --no-crop
# explicit SIRIL crop box: X Y W H (x from left, y from top)
aporn-tool dso-mosaic --in "/data/M31" --out /data/out --target M31 --crop "162 108 2310 4378"
6. Check status and resume
Re-running the same command auto-resumes at the first unfinished stage โ nothing done is repeated. Inspect the ledger any time:
aporn-tool status --out /data/out --target M31
dso-mosaic / M31 (fingerprint 8cd20ff7d100564d)
calibrate done
register done
stack done
spcc done
bge done
denoise done
finish failed
Resume at: finish
7. Re-run specific stages (cheap, from the golden anchor)
# redo colour calibration and everything after it (e.g. once the right Gaia region is installed)
aporn-tool dso-mosaic --in "/data/M31" --out /data/out --target M31 --redo spcc
# restart at a named stage
aporn-tool dso-mosaic --in "/data/M31" --out /data/out --target M31 --from finish
# ignore all checkpoints and re-run everything
aporn-tool dso-mosaic --in "/data/M31" --out /data/out --target M31 --force
8. The advanced, fully-specified run
aporn-tool dso-mosaic \
--in "/data/M31/2026-07-04" --in "/data/M31/2026-07-05" \
--out /data/out \
--target M31 \
--crop "162 108 2310 4378" \
--star-reduce 0.35 \
--config /data/my-aporntool.config.json \
--redo spcc
--star-reduce 0.35โ after StarNet removes stars (mosaic), blend 35% of them back (lower = fewer/dimmer stars; default 0.5).--configโ use a specific config file (tool paths, catalogs, Seestar defaults).--redo spccโ recompute from thespccstage onward, reusing the stack.
Command reference
aporn-tool <command> [options]
| Command | Purpose |
|---|---|
dso-mosaic, dso-emission-nebula, dso-reflection-nebula, dso-star-cluster |
process a target |
config --check [--config PATH] |
show tool discovery; write a starter config |
status --out PATH --target NAME |
print the resume ledger |
--version |
print the version |
Options for the processing modes:
| Option | Description |
|---|---|
--in PATH |
subs folder โ required, repeatable for multi-night |
--out PATH |
output root โ optional; defaults to a <TARGET> folder beside the subs; must be space-free |
--target NAME |
object name โ optional; auto-detected from the subs' FITS OBJECT header |
--crop "X Y W H" |
explicit SIRIL crop box (default: auto-crop) |
--no-crop |
disable auto-crop; keep the full frame |
--star-reduce F |
mosaic star blend-back fraction (default 0.5) |
--clean |
on success, delete working files except the golden anchor (reclaims disk) |
--from STAGE |
restart at this stage |
--redo STAGE |
re-run this stage + everything downstream |
--force |
re-run all stages, ignore checkpoints |
--preflight-only |
validate the environment, then stop |
--config PATH |
config file (default aporntool.config.json) |
Catalogued targets get canonical coordinates: M3 M4 M5 M6 M7 M8 M11 M13 M15 M16 M20 M22 M31 M33 M42 M44 M45 M51 M81 M92 M101 NGC869 NGC6960 NGC7000. Anything else uses the RA/DEC from your subs'
FITS header automatically.
Stages & resume
Stage names are per-mode โ run aporn-tool status to see yours. Current orders:
| Mode | Stages (what --from / --redo accept) |
|---|---|
dso-mosaic |
calibrate โ register โ stack โ spcc โ bge โ denoise โ finish |
dso-reflection-nebula |
calibrate โ register โ stack โ spcc โ bge โ denoise โ finish |
dso-emission-nebula |
calibrate โ register โ stack โ mirrorx โ finish |
dso-star-cluster |
calibrate โ register โ stack โ mirrorx โ finish |
A stage is only marked done after its output is verified (exists, non-empty, right type). A
crash or a raising stage is marked failed and reported with the log tail โ re-run to resume.
Changing a parameter re-runs that stage and everything downstream, nothing upstream.
Output layout
<OUT>/ โ DELIVERABLES ONLY
โโ M31_final.tif โ 16-bit, the real deliverable
โโ M31_final.png
โโ M31_final.jpg โ quick-look
โโ M31_final.fits
โโ _work/ โ everything scratch, hidden here
โโ M31/
โโ 00_lights/ hardlinked .fit subs
โโ 01_process/ SIRIL sequences + frames
โโ 02_linear/ M31_Linear.fit โ GOLDEN ANCHOR (never deleted)
โโ 03_graxpert/ BGE / denoise intermediates
โโ 05_finish/ finish scratch
โโ logs/ generated .ssf + per-stage stdout
โโ aporntool.json run manifest / resume state
Multiple targets can share one --out โ each gets its own _work/<target>/ and deliverables at
the root.
Configuration file
aporn-tool config --check writes aporntool.config.json. Edit it to pin tool paths or set the
local Gaia catalogs:
{
"tool_paths": {
"siril": "/Applications/Siril.app/Contents/MacOS/siril-cli",
"graxpert": "/Applications/GraXpert.app/Contents/MacOS/GraXpert",
"starnet2": "/usr/local/bin/starnet2",
"ffmpeg": "/opt/homebrew/bin/ffmpeg"
},
"seestar_focal_mm": 150.0,
"seestar_pixel_um": 2.9,
"catalog_astro": null,
"catalog_photo": null
}
Resolution order for each tool: explicit config path โ PATH โ known install locations.
FAQ
Do I need darks/flats/bias? No โ the Seestar calibrates internally. For other rigs, calibrate
your subs beforehand (the tool ingests already-calibrated .fit).
How many subs do I need? More integration is the biggest quality lever. Combine nights with
repeated --in. There's no hard minimum, but a handful of subs won't stack meaningfully.
Will it delete my raw data? No. Subs are hardlinked into _work/00_lights/; the golden linear
stack and your originals are never overwritten.
Can I re-process without re-stacking? Yes โ that's the point of the golden anchor. Tweak a
finishing parameter and re-run; only finish (or the changed stage onward) recomputes.
It didn't crop the mosaic tightly. Auto-crop trims the black/near-empty borders, but it
thresholds on brightness rather than per-pixel coverage, so a noisy low-coverage band can survive
on mosaics (those edge pixels are normalized to near the sky level). Pass an explicit
--crop "X Y W H", or do the final crop by hand in Canva/Photoshop. Tighter mosaic cropping is on
the roadmap.
Which mode for the Pleiades (M45)? It's an open cluster in blue reflection nebulosity โ use
dso-reflection-nebula, not the plain cluster finish.
Does it need a GPU? No. GraXpert uses the GPU when available and falls back to CPU.
Troubleshooting
| Symptom | Fix |
|---|---|
no StarNet executable set (mosaic) |
Set StarNet in SIRIL โ Preferences โ External Programs. PATH alone isn't enough for mosaic. |
| Preflight: GraXpert models missing (but installed) | Open GraXpert, run BGE + Denoise once so it downloads the model files. |
| SPCC "no stars" / imprecise | Install the Gaia region matching the target (Milky Way vs Galaxy Season). |
--out path must not contain spaces |
Choose a space-free output folder (SIRIL limitation). |
golden anchor not found on --from/--redo |
Run the full pipeline first (drop --from/--redo) so preprocess produces the anchor. |
| Plate-solving fails on a mosaic | Ensure SIRIL's astrometry catalog is installed; the tool verifies solves by frame count, not exit code. |
| Noisy/ragged band left after auto-crop | Known mosaic limitation (brightness-based crop misses low-coverage edges) โ pass --crop "X Y W H", or crop in post. |
Every stage saves its SIRIL script and stdout under _work/<target>/logs/ โ check <stage>.log
for the exact tool error.
Limitations
- Deconvolution / wavelets are skipped for undersampled Seestar data (they amplify noise without adding real detail).
- The last 5% โ final crop, curves, watermark โ is done by hand from the 16-bit TIFF.
- Planetary needs a manual AutoStakkert stacking step (no usable CLI) and is not yet shipped.
- Auto-crop trims black borders but can leave a noisy low-coverage band on mosaics (it crops by
brightness, not coverage) โ tighten with
--cropor in post; a coverage-aware crop is planned.
Development
.venv/bin/python -m pip install -e . pytest pytest-timeout
.venv/bin/python -m pytest -q # unit suite; external tools are mocked
Tests mock Siril/GraXpert/StarNet, so they run anywhere. Integration runs need the real tools and real subs. See REQUIREMENTS.md for the full design, the per-mode pipeline contract, and the load-bearing SIRIL/GraXpert/StarNet gotchas.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aporn_tool-0.3.1.tar.gz.
File metadata
- Download URL: aporn_tool-0.3.1.tar.gz
- Upload date:
- Size: 48.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3131734e77f43c54b5672070eeddbdfc761f468007f611cc06ff0f91f9aa9abc
|
|
| MD5 |
89138c2c810d2f4e989386b318f3c37f
|
|
| BLAKE2b-256 |
f63de340dec4c0a9e61fece09ea5685183963cb725250639e30c0377162e442d
|
Provenance
The following attestation bundles were made for aporn_tool-0.3.1.tar.gz:
Publisher:
workflow.yml on christiantroyandrada/aporn-tool
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aporn_tool-0.3.1.tar.gz -
Subject digest:
3131734e77f43c54b5672070eeddbdfc761f468007f611cc06ff0f91f9aa9abc - Sigstore transparency entry: 2087384484
- Sigstore integration time:
-
Permalink:
christiantroyandrada/aporn-tool@c703d32a3cda4379f60a8736117307fbe23a0143 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/christiantroyandrada
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@c703d32a3cda4379f60a8736117307fbe23a0143 -
Trigger Event:
release
-
Statement type:
File details
Details for the file aporn_tool-0.3.1-py3-none-any.whl.
File metadata
- Download URL: aporn_tool-0.3.1-py3-none-any.whl
- Upload date:
- Size: 45.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
41377780e5406c1247536ddacff49ddbc7358c2f030106e123b6b5ed615fb3d1
|
|
| MD5 |
1a7e5c050ac4d26900bb0550cff814f0
|
|
| BLAKE2b-256 |
f20a2bc8748ae15fdd02fdde396e38481d2ca8af4b46118b76871aecf2504c9c
|
Provenance
The following attestation bundles were made for aporn_tool-0.3.1-py3-none-any.whl:
Publisher:
workflow.yml on christiantroyandrada/aporn-tool
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aporn_tool-0.3.1-py3-none-any.whl -
Subject digest:
41377780e5406c1247536ddacff49ddbc7358c2f030106e123b6b5ed615fb3d1 - Sigstore transparency entry: 2087384521
- Sigstore integration time:
-
Permalink:
christiantroyandrada/aporn-tool@c703d32a3cda4379f60a8736117307fbe23a0143 -
Branch / Tag:
refs/tags/v0.3.1 - Owner: https://github.com/christiantroyandrada
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
workflow.yml@c703d32a3cda4379f60a8736117307fbe23a0143 -
Trigger Event:
release
-
Statement type: