Metadata-Version: 2.4
Name: termfits
Version: 0.1.0
Summary: A terminal FITS image viewer with zoom and pan, tuned for radio images
Author-email: Ian Heywood <email.ian.heywood@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/ianheywood/termfits
Project-URL: Issues, https://github.com/ianheywood/termfits/issues
Keywords: fits,astronomy,radio astronomy,terminal,viewer,tui
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Astronomy
Classifier: Topic :: Scientific/Engineering :: Visualization
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.21
Requires-Dist: astropy>=5.0
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Dynamic: license-file

# termfits

A FITS image viewer that runs in your terminal, built for quick looks at radio images
over SSH: no X forwarding, no VNC, no copying files back to your laptop.

![termfits showing a radio field: default limits vs a tightened upper limit](https://raw.githubusercontent.com/ianheywood/termfits/main/docs/images/radio_sigma_limits.png)

It draws the image with Unicode half-block characters in 24-bit colour. Each character
cell holds two pixels, so a 100 x 100 character window shows a 100 x 200 grid of
roughly square pixels. You can zoom, pan, step through cube planes and switch extensions,
and the status bar reports the pixel value, its significance in sigma, the position
in RA/Dec, the noise and the beam.

## Install

```bash
pip install termfits
```

This installs a `termfits` command. termfits needs Python 3.9+, numpy and astropy, and
a terminal with truecolour support: most current terminals have it, including iTerm2,
GNOME Terminal, Konsole, kitty, WezTerm, Alacritty, Windows Terminal (via WSL) and tmux
with `set -g default-terminal "tmux-256color"` and `set -as terminal-features ",*:RGB"`.
It runs on Linux and macOS, or under WSL on Windows.

## Use

```bash
termfits image.fits
termfits cube.fits --plane 120 --cmap inferno
termfits image.fits --sigma-hi 4           # tighter limits for faint emission
termfits image.fits --width 100 --height 50  # square-ish view for a square image
```

Make your terminal at least 100 columns by 104 rows for the default view (100 x 100
plus a 4-line status bar), or set `--width` / `--height` to fit. Terminal characters are
about twice as tall as they are wide, so `--height` set to half of `--width` gives a
square viewing area.

Press `?` in the viewer for the key list.

| Keys | Action |
|---|---|
| arrows or `h j k l` | pan by 10% of the view |
| Shift+arrows or `H J K L` | pan by half the view |
| `+` / `-` | zoom in / out |
| `f` / `1` / `r` | fit image / 1:1 / reset |
| `<` `>` | lower / raise the upper display limit (sigma) |
| `(` `)` | lower / raise the lower display limit (sigma) |
| `c` | cycle clip mode: sigma, 99.5, 99, 98, 95 %, min/max |
| `n` / `N` | measure the noise in the current view / back to the whole image |
| `b` | zoomed-out binning: block mean / block max |
| `m` / `M` | next / previous colour map |
| `s` | cycle stretch: linear, sqrt, log, asinh |
| `e` / `x` | toggle beam ellipse / crosshair |
| `[` `]` | previous / next plane (channel, Stokes) |
| `Tab` | next image extension |
| `q` or `Esc` | quit |

`termfits image.fits --snapshot` prints one frame to stdout and exits, which is handy
for logs or notebooks.

## Radio defaults

- **Display limits from the noise.** The rms is estimated as 1.4826 x the median
  absolute deviation, which bright sources barely affect. The default range is -1 to
  +10 sigma about the median. Press `n` to re-measure the noise from the region in view
  when it varies across the field (e.g. towards the primary-beam edge).
- **Values in sigma.** The status bar shows the pixel under the crosshair in BUNIT
  (scaled to Jy, mJy or µJy so the rms reads sensibly) and in sigma.
- **Beam.** BMAJ/BMIN/BPA from the header are shown in arcsec and pixels, and the beam
  is drawn in the lower-left corner once it is large enough to see.
- **Blanked pixels** (NaNs, e.g. outside a primary-beam cut) are drawn in a muted purple
  so they are not mistaken for zero.

## Zoomed out: what you can and can't trust

When the image is larger than the view, blocks of pixels are combined into one screen
pixel; none are skipped.

![Block mean vs block max for the whole field](https://raw.githubusercontent.com/ianheywood/termfits/main/docs/images/mean_vs_max.png)

- **Block mean** (default) lowers the noise, so faint *extended* emission stands out,
  but faint *compact* sources are diluted and can vanish.
- **Block max** (`b`) keeps every compact source visible, but it also lifts the
  background: the maximum of many noise pixels sits around +3 sigma, so noise peaks
  look like sources.

Use either for finding candidates, then press `1` to inspect them at full resolution.
For decisions about marginal detections, use a full-resolution viewer such as CARTA
or DS9.

## Colour maps

![The ten colour maps on the same field](https://raw.githubusercontent.com/ianheywood/termfits/main/docs/images/colourmaps.png)

`inverted` (default) and `gray`; `heat` (DS9-style); `inferno`, `magma`, `viridis` and
`cubehelix`, which are perceptually uniform and the best colour choices for faint
features; `cool`; `rainbow` (turbo-like, good for bright high-dynamic-range regions,
but it creates false contours in noise); and `diverging`, which is centred on the
median so negative sidelobes show blue and positive emission red.

Truecolour gives the full range; `--256` is a fallback for terminals without it, and
colour maps get coarse there (6 levels per channel).

## Data support

- 2-D images, and cubes of any dimension: extra axes (FREQ, STOKES, ...) are
  flattened into planes stepped with `[` and `]`.
- Multi-extension files: every image HDU is reachable with `Tab`.
- RA/Dec readout from the celestial WCS via astropy.
- Large images: one plane is loaded into memory as float32 (about 400 MB for
  10k x 10k). The first zoom-out builds the binned copies, which takes a few seconds;
  after that it is quick.
- If astropy is missing, a built-in reader handles plain uncompressed images, without
  WCS.

Not yet supported: per-channel beams stored in a CASA `BEAMS` table (the header beam is
used), and tile-compressed images without astropy.

## Development

```bash
git clone https://github.com/ianheywood/termfits
cd termfits
pip install -e ".[test]"
pytest
```

The screenshots in `docs/images` were rendered from termfits' own output, using
synthetic data.

## Licence

MIT. See [LICENSE](LICENSE).
