Python library that converts astronomical FITS images into color/grayscale images.
Project description
trilogy
Python library for converting astronomical FITS images into beautiful color or grayscale images.
Cosmic Horseshoe gravitational lens imaged by Hubble WFC3
RGB composite using filters F475W (blue), F606W (green), and F814W (red)
Installation
pip install trilogy
Or from source:
git clone https://github.com/oliveirara/trilogy.git
cd trilogy
pip install -e .
Quick Start
Grayscale Image (Single Band)
from trilogy import Trilogy
trilogy = Trilogy(
images="galaxy.fits",
outname="galaxy"
)
img = trilogy.save() # Saves galaxy.png
RGB Color Image (Multi-Band)
from trilogy import Trilogy
trilogy = Trilogy(
images={
"R": ["i_band.fits"],
"G": ["r_band.fits"],
"B": ["g_band.fits"]
},
outname="galaxy_rgb"
)
img = trilogy.save() # Saves galaxy_rgb.png
Adjusting Brightness and Contrast
trilogy = Trilogy(
images="galaxy.fits",
outname="galaxy_adjusted",
noiselum=0.10, # Lower = darker background (0.05-0.5)
satpercent=0.0001 # Lower = less saturation (0.0001-0.01)
)
Boosting Colors (RGB)
trilogy = Trilogy(
images={"R": ["r.fits"], "G": ["g.fits"], "B": ["b.fits"]},
outname="colorful",
colorsatfac=1.5 # >1 boosts colors, <1 reduces
)
Key Parameters
Essential Parameters
| Parameter | Default | Range | Effect |
|---|---|---|---|
noiselum |
0.15 | 0.05-0.5 | Background brightness (lower = darker) |
satpercent |
0.001 | 0.0001-0.01 | % of pixels to saturate (lower = more detail) |
colorsatfac |
1.0 | 0.5-2.0 | Color saturation (RGB only, >1 = more vivid) |
Common Adjustments
Image too dark?
noiselum=0.25 # Increase background brightness
satpercent=0.005 # Allow more saturation
Image too bright/washed out?
noiselum=0.08 # Decrease background brightness
satpercent=0.0001 # Preserve more highlights
Colors too weak? (RGB only)
colorsatfac=1.5 # Boost color saturation
Advanced Usage
Per-Channel Control (RGB)
trilogy = Trilogy(
images={"R": [...], "G": [...], "B": [...]},
noiselums={
"R": 0.20, # Brighter red channel
"G": 0.15, # Balanced green
"B": 0.10 # Darker blue channel
}
)
Combining Multiple Images
# Average multiple exposures
trilogy = Trilogy(
images=["exposure1.fits", "exposure2.fits", "exposure3.fits"],
combine="average"
)
# Or sum them
trilogy = Trilogy(
images=["exp1.fits", "exp2.fits"],
combine="sum"
)
Using Configuration Object
from trilogy import Trilogy, TrilogyConfig
config = TrilogyConfig(
noiselum=0.12,
satpercent=0.0005,
colorsatfac=1.3,
samplesize=2000,
combine="average"
)
trilogy = Trilogy(images=my_images, config=config)
Manual Control (Disable Auto-Adjustment)
By default, trilogy automatically adjusts problematic parameters. To use exact values:
trilogy = Trilogy(
images="galaxy.fits",
noiselum=0.147, # Will use exactly this value
satpercent=0.000823,
auto_adjust=False # Disable auto-adjustment
)
Use auto_adjust=False when:
- Replicating results from papers/publications
- You've manually fine-tuned parameters visually
- You need specific parameter values
Jupyter Notebooks
Trilogy works seamlessly in notebooks:
from trilogy import Trilogy
t = Trilogy(images="galaxy.fits", noiselum=0.15)
img = t.run() # Returns PIL Image, displays automatically
See examples/with_notebook/notebook.ipynb for complete examples.
Examples
The examples/ directory contains real FITS data from various surveys:
- cosmic_horseshoe/ - HST WFC3 RGB composite
- single_band/ - Grayscale examples (CS82, CFHTLenS, DES)
- multiple_bands/ - RGB examples (HSC, KIDS, Legacy Survey, RCSLenS)
- with_notebook/ - Jupyter notebook examples
Each example includes a example.py script you can run:
cd examples/cosmic_horseshoe
python example.py
All Parameters
Input/Output
images: str, list, or dict - Input FITS file(s)indir: Path - Input directory (default: current directory)outdir: Path - Output directory (default: current directory)outname: str - Output filename without extension
Image Scaling
noiselum: float - Noise luminosity 0-1 (default: 0.15)noiselums: dict - Per-channel noise luminositysatpercent: float - Percentage of pixels to saturate (default: 0.001)colorsatfac: float - Color saturation factor (default: 1.0)noisesig: float - Noise sigma for output (default: 1.0)noisesig0: float - Noise sigma for measurement (default: 2.0)correctbias: bool - Correct for background bias (default: False)
Processing
combine: "average" or "sum" - How to combine multiple images (default: "average")samplesize: int - Sample region size for determining scaling (default: 1000)stampsize: int - Processing stamp size (default: 1000)maxstampsize: int - Maximum stamp size (default: 6000)bscale: float - Multiply all pixel values (default: 1.0)bzero: float - Add to all pixel values (default: 0.0)
Advanced
auto_adjust: bool - Automatically adjust problematic parameters (default: True)noise: float - Manual noise level (overrides automatic detection)saturate: float - Manual saturation level (overrides automatic)invert: bool - Invert luminosity (default: False)legend: bool - Add filter legend to RGB images (default: False)
Supported File Formats
- Standard FITS (
.fits) - Compressed FITS (
.fits.gz,.fits.fz) - Multi-extension FITS (specify with
image.fits[1]) - Automatic extension detection
Requirements
- Python >= 3.11
- astropy >= 6.1.7
- numpy >= 2.2.4
- pillow >= 11.2.1
- scipy >= 1.15.2
Troubleshooting
Image is too dark
trilogy = Trilogy(images=..., noiselum=0.25, satpercent=0.005)
Image is too bright/washed out
trilogy = Trilogy(images=..., noiselum=0.08, satpercent=0.0001)
Colors are too weak (RGB)
trilogy = Trilogy(images=..., colorsatfac=1.5)
Getting warnings about parameter adjustments
The auto-adjustment system is helping you avoid problematic values. To use exact values:
trilogy = Trilogy(images=..., your_params, auto_adjust=False)
Image appears blank/black
- Check that FITS files have data (not empty)
- Try increasing
noiselumandsatpercent - Verify FITS extension if using multi-extension files
Images with many NaN pixels
Trilogy automatically handles NaN (Not a Number) and Inf pixels:
✅ Smart NaN handling:
- NaN/Inf pixels are set to 0 (black) in output
- Statistics calculated only from valid pixels
- Warning displayed if >10% of pixels are NaN
- No crashes or numerical errors from NaN pixels
Example output:
⚠️ Warning: 50.0% of pixels are NaN/Inf
Recommendation:
- Images with >50% NaN pixels will appear mostly black
- Use quality filtering before processing (e.g.,
filter-empty-cutouts.py) - Consider cropping to regions with valid data
Why NaN pixels occur:
- Edge effects from mosaicking/reprojection
- Missing detector data
- Masked pixels in processed images
- Image cutouts extending beyond original field
Resources
- Lupton's Algorithm: http://www.astro.princeton.edu/~rhl/PrettyPictures/
- Original Trilogy: https://www.stsci.edu/~dcoe/trilogy/
- ASCL Entry: https://ascl.net/1508.009
Citation
If you use trilogy in your research, please cite:
@MISC{2012ascl.soft08009C,
author = {{Coe}, D.},
title = "{Trilogy: Image composition software}",
keywords = {Software},
year = 2012,
month = aug,
eid = {ascl:1508.009},
pages = {ascl:1508.009},
archivePrefix = {ascl},
eprint = {1508.009},
adsurl = {https://ui.adsabs.harvard.edu/abs/2012ascl.soft08009C},
}
License
See LICENSE file.
Contributing
Contributions are welcome! Please open an issue or pull request on GitHub.
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 trilogy-1.0.0.tar.gz.
File metadata
- Download URL: trilogy-1.0.0.tar.gz
- Upload date:
- Size: 39.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b0b4fd87fd4fcbdd8f56402ad0d7d8600b4f551755e7bbb06232f26b32147a78
|
|
| MD5 |
fe6108019b6f3e7f43342afa532a4a5a
|
|
| BLAKE2b-256 |
20dcb7b9f71826f150f019fb462401fbd4e350a92610f6265ae4a6013d870a5d
|
Provenance
The following attestation bundles were made for trilogy-1.0.0.tar.gz:
Publisher:
publish.yml on oliveirara/trilogy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
trilogy-1.0.0.tar.gz -
Subject digest:
b0b4fd87fd4fcbdd8f56402ad0d7d8600b4f551755e7bbb06232f26b32147a78 - Sigstore transparency entry: 597625831
- Sigstore integration time:
-
Permalink:
oliveirara/trilogy@9d613dc32805e3413d2bf88081481f5adbba76f5 -
Branch / Tag:
refs/tags/1.0.0 - Owner: https://github.com/oliveirara
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9d613dc32805e3413d2bf88081481f5adbba76f5 -
Trigger Event:
release
-
Statement type:
File details
Details for the file trilogy-1.0.0-py3-none-any.whl.
File metadata
- Download URL: trilogy-1.0.0-py3-none-any.whl
- Upload date:
- Size: 37.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8302c38e8e17220fbc0b241b491c2a5cfb763f834455d6e56982c90b71e1a678
|
|
| MD5 |
718f4505e2dedd74643506b486a13a81
|
|
| BLAKE2b-256 |
dedcc20c97dc8bb58c31256a7f28383f49490606d6a4a45bfadf7ac269972add
|
Provenance
The following attestation bundles were made for trilogy-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on oliveirara/trilogy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
trilogy-1.0.0-py3-none-any.whl -
Subject digest:
8302c38e8e17220fbc0b241b491c2a5cfb763f834455d6e56982c90b71e1a678 - Sigstore transparency entry: 597625833
- Sigstore integration time:
-
Permalink:
oliveirara/trilogy@9d613dc32805e3413d2bf88081481f5adbba76f5 -
Branch / Tag:
refs/tags/1.0.0 - Owner: https://github.com/oliveirara
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9d613dc32805e3413d2bf88081481f5adbba76f5 -
Trigger Event:
release
-
Statement type: