stretch

package
v1.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 14, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package stretch implements image stretching operations for FITS astronomical images. These are the same algorithms used by Siril, ported to pure Go and cross-validated against siril-cli output on a comprehensive golden fixture set.

The package operates on normalized float32 pixel data in [0, 1], where 0 is black and 1 is the maximum signal. Multi-channel images are represented as separate float32 slices per channel.

Operations

  • Linear — clip at black point, linear scale to [0, 1]
  • Asinh — inverse hyperbolic sine stretch (preserves color ratios)
  • MTF — Midtone Transfer Function (KStars/PixInsight autostretch)
  • GHT — Generalized Hyperbolic Transformation (Strasser 2022)
  • [CLAHE] — Contrast Limited Adaptive Histogram Equalization

Validation

Every operation is cross-validated against siril-cli 1.5.0. The golden fixtures in stretch/testdata/golden/ contain per-pixel float32 output from Siril for a synthetic 3-channel 64×48 test image.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Asinh

func Asinh(pixels []float32, beta, offset float32)

Asinh applies an inverse-hyperbolic-sine stretch to a single channel (mono image). For each pixel:

xprime = max(0, (x - offset) / (1 - offset))
output = clamp(asinh(beta * xprime) / asinh(beta), 0, 1)

Port of Siril's asinh command (asinh.c). For multi-channel images, use AsinhRGB which preserves color ratios via luminance-based scaling.

func AsinhRGB

func AsinhRGB(r, g, b []float32, beta, offset float32)

AsinhRGB applies an asinh stretch to a 3-channel RGB image, preserving color ratios by stretching the luminance and scaling all channels proportionally. This matches Siril's default asinh behavior for color images (RGBBLEND mode with equal luminance weights).

r, g, b must have the same length (one element per pixel).

func Autostretch

func Autostretch(pixels []float32, shadowsClip, targetBG float32)

Autostretch applies the full autostretch pipeline to a single channel: compute statistics, derive MTF parameters, apply the transfer function.

func AutostretchLinked

func AutostretchLinked(channels [][]float32, shadowsClip, targetBG float32)

AutostretchLinked applies the linked autostretch pipeline to an RGB image: compute averaged statistics, derive one set of MTF parameters, apply the same transfer function to all channels.

func AutostretchLinkedParams

func AutostretchLinkedParams(channels [][]float32, shadowsClip, targetBG float32) (shadows, midtones, highlights float32)

AutostretchLinkedParams computes linked MTF parameters by averaging statistics across all channels. Each channel is a separate []float32.

Port of Siril's find_linked_midtones_balance (mtf.c:259).

func AutostretchParams

func AutostretchParams(pixels []float32, shadowsClip, targetBG float32) (shadows, midtones, highlights float32)

AutostretchParams computes the MTF parameters (shadows, midtones, highlights) from image statistics and the given shadow clipping and target background values.

shadowsClip is negative (e.g. -2.8) — the number of MAD-normalized sigma below the median to place the shadow clip point. targetBG is the target midtone value (e.g. 0.25).

Port of Siril's find_unlinked_midtones_balance (mtf.c:374). Siril excludes pixels that are exactly 0.0 or NaN from the median and MAD computation (statistics_float.c reassign_to_non_null_data).

func CLAHERGB

func CLAHERGB(r, g, b []float32, nx, ny, tileGridSize int, clipLimit float64)

CLAHERGB applies CLAHE to a 3-channel RGB image using the CIE Lab color space, matching the OpenCV/Siril pipeline. The L channel is quantized to uint8, CLAHE'd, then the result is converted back to float32 RGB.

func EncodeJPEG

func EncodeJPEG(w io.Writer, img *image.RGBA, quality int) error

EncodeJPEG writes the RGB image as a JPEG to w with the given quality.

func GHT

func GHT(pixels []float32, params GHTParams)

GHT applies the Generalized Hyperbolic Transformation to a pixel array. The stretch is applied independently per channel.

The user-supplied D is transformed internally: actualD = expm1(D) = e^D - 1. This matches Siril's parameter convention (command.c:3316).

func Linear

func Linear(pixels []float32, bp float32)

Linear applies a linear stretch with black point clipping. For each pixel: output = max(0, (input - bp) / (1 - bp)).

Port of Siril's linstretch command (ght.c:291, a special case of GHT with D=0, B=0). Applied independently per channel.

func MTF

func MTF(pixels []float32, shadows, midtones, highlights float32)

MTF applies the Midtone Transfer Function to a single channel. This is the core of Siril's autostretch command.

MTF(x, m) = (m - 1) * x / ((2*m - 1) * x - m)

where x is the input pixel (rescaled to [0, 1] after shadow/highlight clipping) and m is the midtone balance (0 < m < 1; m=0.5 is identity).

shadows is the black clip point (pixels <= shadows become 0). midtones is the midtone balance parameter. highlights is the white clip point (pixels >= highlights become 1).

Port of Siril's MTF function (mtf.c:95).

func MTFValue

func MTFValue(x, m float32) float32

MTFValue evaluates the MTF formula at a single point. Exported for testing and direct use.

func NormalizeChannels added in v1.0.1

func NormalizeChannels(channels ...[]float32)

NormalizeChannels scales all channels to [0, 1] using a single shared min/max across every channel. This preserves the relative intensity between channels, which matters for stretches that operate on color ratios (linked autostretch, asinh RGB).

If the data is already in [0, 1] (common for calibrated float FITS), this is effectively a no-op.

func ToRGBImage

func ToRGBImage(r, g, b []float32, nx, ny int) *image.RGBA

ToRGBImage converts stretched float32 channels to a standard Go image.RGBA. Each pixel is quantized from [0, 1] to [0, 255].

Types

type GHTParams

type GHTParams struct {
	D  float64 // Stretch intensity (user-space; internally transformed to expm1(D))
	B  float64 // Localisation parameter (0 = exponential, -1 = log, >0 = power)
	LP float64 // Low protection point [0, SP]
	SP float64 // Symmetry point [0, 1]
	HP float64 // High protection point [SP, 1]
	BP float64 // Black point (only for linear type)
}

GHTParams holds the parameters for the Generalized Hyperbolic Transformation (Strasser 2022). Port of Siril's ght command (ght.c).

type Preset

type Preset struct {
	Name    string
	Label   string
	ApplyFn func(r, g, b []float32, nx, ny int)
}

Preset identifies a named stretch configuration matching the fitview presets. Each preset maps to a specific stretch function with fixed parameters.

func DefaultPresets

func DefaultPresets() []Preset

DefaultPresets returns the 18 standard presets matching fitview's siril.go preset list.

func FindPreset

func FindPreset(name string) (Preset, error)

FindPreset returns the preset with the given name, or an error if not found.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL