Skip to content

FITS: astrophotos to TIFF, PNG and JPG (#460) - #505

Merged
KnifeLemon merged 3 commits into
mainfrom
feature/460-fits
Oct 24, 2026
Merged

KnifeLemon merged 3 commits into
mainfrom
feature/460-fits

Conversation

@KnifeLemon

Copy link
Copy Markdown
Owner

Filee reads FITS pictures (.fits, .fts) from telescope cameras and capture/stacking programs, as planned in #460. .fit is not claimed: it stays free for Garmin FIT (#451).

What it does

Reading (Magick/Fits.cs, written from the FITS 4.0 standard, no new dependency)

  • 2880-byte blocks of 80-character header cards, big-endian data, BITPIX 8 / 16 / 32 / 64 / -32 / -64, physical values = BZERO + BSCALE × stored (BLANK integers become undefined).
  • The primary HDU or, when it is empty, the first IMAGE extension. A cube with NAXIS3 = 3 is red, green and blue; other cubes give their first plane.
  • Bayer pattern from BAYERPAT, else COLORTYP (RGGB, BGGR, GRBG, GBRG; case and padding ignored), at the first stored pixel and shifted by XBAYROFF / YBAYROFF. Keywords missing from an extension are looked up in the primary header.
  • ROWORDER = 'BOTTOM-UP' turns the picture upright. Without it, rows are taken top-down, as capture programs write them.
  • Tile-compressed files (fpack, a BINTABLE with ZIMAGE = T) fail with a message asking to funpack them. Damaged or cut-short files fail with a reason.

FITS → PNG / JPG (and every other picture target) (Magick/FitsRendering.cs)

  • Automatic stretch, like a capture program's preview: values scaled to 0–1 over their range; per channel, shadows clipped 2.8 normalized MADs below the median, then a midtones transfer function takes the median to 0.2.
  • A colour camera's mosaic is debayered first (bilinear) with the header's pattern. A new preset option, Debayer FITS from a colour camera (on by default), keeps it mono when turned off. Channels are stretched one by one, so the sky comes out neutral. Mono files and three-plane colour files are never debayered.
  • The result is 8-bit and then goes through ImageMagick as usual (resize, rotate, quality, …). PDF, ICNS, HEIC and SVG are reached the same way.

FITS → TIFF (Magick/TiffWriter.cs, our own small baseline TIFF writer, because the Q8 ImageMagick stores only 8 bits)

  • New preset option FITS (astronomy) to TIFF: Linear: the data as it is (default) or Stretched, like a preview.
  • The TIFF follows the file's BITPIX: 8-bit stays 8-bit and 16-bit stays 16-bit (BZERO/BSCALE applied, so unsigned 16-bit comes out as unsigned 16-bit). 32-bit integer and floating-point data (BITPIX 32, 64, -32, -64), and 8/16-bit data that doesn't fit the unsigned range (negative or scaled values), become 32-bit floating point, so nothing is cut.
  • Never debayered: the mosaic stays intact for the program that processes it.
  • Uncompressed, LZW or Deflate, following the preset's TIFF compression (integer data uses the horizontal predictor). Rotate and flip apply. Resizing and the other picture options don't.
  • FITS → TIFF costs 11 instead of 10, so other targets are always reached through the stretched picture and never through the linear TIFF.

Settings and UI

  • ImageOptions.FitsTiff (lenient enum) and ImageOptions.FitsDebayer (default true; presets saved before this read as linear TIFF and debayer). Both are shown in the preset editor's image card: the TIFF choice for TIFF targets, the checkbox for the other picture targets.
  • Settings schema 43 teaches the images donut .fits and .fts without taking them from a donut of the user's. Schema 42 is the print studio from feat(spreadsheet): preview and edit XLSX page layout before PDF export #437.
  • The six new strings are translated in all 14 language files (Korean in 해요체).
  • Docs: docs/ENGINES.md (table row and a FITS section), and the 13 READMEs (FITS in the images row, 255 → 256 formats).

What it doesn't do

  • No 16-bit PNG: the bundled ImageMagick is Q8, so PNG/JPG are 8-bit and stretched.
  • No tile-compressed (fpack .fz) or table-only FITS. No data cubes beyond three colour planes (the first plane is taken).
  • No debayering in the TIFF, and no CFA tags in it. No FITS header copied into the TIFF.
  • A linear float TIFF keeps the values as they are. ADU-range floats (for example 0–65535) look white in viewers that expect 0–1.
  • The stretch is median/MAD based like N.I.N.A.'s preview, with similar defaults (−2.8, 0.2), but it is not guaranteed to match it pixel for pixel. It is unlinked (per channel) for colour.
  • Row order: without a ROWORDER keyword, files are taken as top-down (what N.I.N.A., MaxIm DL and similar capture programs write; PixInsight's default view). DS9-style bottom-up viewers would show such a file flipped. The Bayer pattern is read at the first stored pixel.
  • The interactive Crop tool still reads through ImageMagick directly (as it does for DICOM).

Tests

  • tests/Filee.Engines.Tests/FitsTests.cs (25 tests; FITS and TIFF files are generated in the tests):
    • BITPIX 8, 16 with BZERO 32768, 32, -32 and -64 into linear TIFF, checked sample by sample.
    • Signed 16-bit with negative values, written as float.
    • LZW and Deflate TIFFs read back by libtiff, including LZW strips of random data that reset the code table.
    • A stretched 16-bit TIFF and a stretched mono PNG.
    • An RGGB mosaic debayered with each of the four patterns, the offsets and COLORTYP; the same mosaic kept mono; the linear TIFF keeping it.
    • A three-plane colour file, ROWORDER, rotation of a TIFF, header parsing (D exponents, quoted strings, repeated keywords), an IMAGE extension, fpack / not-FITS / truncated files, the stretch math, and the routes (FITS → PDF never via TIFF).
  • tests/Filee.Core.Tests/FitsFormatTests.cs: extension detection (.fit is not FITS), the images donut, the schema 43 migration keeping a custom claim, and old/new preset JSON.
  • RenderTests.Preset_editor_shows_and_saves_the_fits_options (en, ko).
  • Local results: Core 546 passed; Engines FITS plus the image, PDF, tracing, DICOM, multi-page TIFF and JPEG XR suites, 132 passed; App Localization + Preset, 168 passed. dotnet format --verify-no-changes is clean.
  • Release timing on a 6248 × 4176 Bayer frame: TIFF about 1.2 s; PNG about 8 s, mostly ImageMagick's PNG encoder.

Closes #460

🤖 Generated with Claude Code

KnifeLemon and others added 3 commits October 24, 2026 03:10
…th (#460)

FITS (.fits, .fts; ".fit" stays for Garmin FIT) is read by Filee's own
reader (Magick/Fits.cs): 2880-byte blocks of 80-character cards,
big-endian data, BITPIX 8/16/32/64/-32/-64 with BZERO/BSCALE and BLANK,
the primary HDU or the first IMAGE extension, three-plane colour cubes,
BAYERPAT/COLORTYP with XBAYROFF/YBAYROFF and ROWORDER. Tile-compressed
files (fpack) fail with a message asking to funpack them.

PNG, JPG and every other picture are stretched automatically (median /
MAD shadows clipping at -2.8, midtones transfer to a 0.2 background,
per channel) and a colour camera's mosaic is debayered bilinearly with
the header's pattern unless the preset keeps it mono.

FITS -> TIFF is written by a small TIFF writer of our own, since the
Q8 ImageMagick stores 8 bits: linear (the data as it is) or stretched,
8-bit and 16-bit data at their depth (unsigned 16-bit through BZERO),
32-bit integer, floating-point and anything that doesn't fit as 32-bit
float; uncompressed, LZW or Deflate as the preset says. The mosaic is
never debayered there.

New preset options ImageOptions.FitsTiff and FitsDebayer, shown in the
image card for TIFF and other picture targets; schema 42 teaches the
images donut the extensions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…es to 43

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@KnifeLemon KnifeLemon added the formats New formats and engines label Oct 23, 2026
@KnifeLemon KnifeLemon self-assigned this Oct 23, 2026
@KnifeLemon
KnifeLemon merged commit da760fd into main Oct 24, 2026
11 checks passed
cre8ive-marketing added a commit to cre8ive-marketing/Filee that referenced this pull request Oct 24, 2026
KnifeLemon#505 (FITS) took settings schema 43, so the USM step is v44
(CurrentSchemaVersion = 44), after the FITS one; its test upgrades a
version 43 library.
KnifeLemon added a commit to xmetropolicex/Filee that referenced this pull request Oct 24, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
KnifeLemon added a commit to flexeykinDev/Filee that referenced this pull request Oct 24, 2026
FITS (KnifeLemon#505) took settings schema 43, so Cut a clip moves to 44.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

formats New formats and engines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Format request: .fits

1 participant