Histogram Viewer

A per-image, read-only analysis view of the intensity distribution, with linear/log axes and a live statistics panel. It measures the image; it does not modify it, so it has no drag-to-canvas grip.

1. Using the tool

The viewer is non-modal and bound to one document — you can keep several open, one per image. The left side shows the histogram; the right side a live statistics table.

ControlWhat it does
Zoom slider (50–1000%)Vertical zoom of the plot — amplifies the count axis so faint, low-population bins (background, faint nebulosity) become visible without changing the data.
Toggle Log X-AxisSwitch the intensity axis between linear and logarithmic, which spreads out the crowded low-intensity end typical of linear astro data.
Toggle Log Y-AxisPlot counts on a log scale so the faint tail is visible next to a tall background peak.
“?” (sensor max)Set your camera's true saturation level (native ADU or normalized) so the High Clipped warning reflects real sensor saturation, not just the 1.0 ceiling.
More Stats…Opens a detailed per-channel table: mean, variance, MAD, IQR and a full percentile set.
Ctrl+Click on the plotSends the clicked intensity as the pivot to the Hyperbolic Stretch tool, if it's open.

The statistics panel

Per channel (R/G/B for colour, or a single grey column for mono): Min, Max, Median, StdDev, MAD, and Low/High Clipped counts with percentages. The clipped cells tint red as the fraction climbs (mild ≥ 0.01%, medium ≥ 0.1%, strong ≥ 1%) so over-exposure or a crushed black point is obvious at a glance.

2. Technical details

What the viewer computes, and the exact statistic definitions in the panel.

2.1 Data domain & channels

The image is read as float in the normalized range [0, 1]. A 3-channel image is analysed as three independent channels (R, G, B); mono and single-channel images as one. Every statistic and histogram below is computed per channel on the full-resolution pixel array.

2.2 Binning

Histograms use 512 bins, cached once per image and only redrawn (not recomputed) on zoom/resize. Two bin edge sets are built so the X axis can switch instantly:

linear: edges = linspace(0, 1, 513) log: eps = max(1e-6, min positive pixel) edges = logspace(log10(eps), log10(1), 513)

Counts are numpy.histogram(channel, bins=edges) per channel. The log edge set starts at the smallest non-zero pixel value ε rather than 0, since log(0) is undefined; true-black pixels fall below the first log bin.

2.3 Axis mapping

On a log X axis, an intensity v is placed at horizontal fraction

x_{\mathrm{frac}}(v)=\frac{\log_{10}v-\log_{10}\epsilon}{-\log_{10}\epsilon}

(since log10(1) = 0). Log Y maps each bin count through log before scaling to the plot height, compressing a dominant background peak so faint structure stays on scale. The zoom slider applies an additional linear gain to the count axis.

2.4 Statistics

For a channel's pixel values with mean μ and N samples: Median is median(x), Variance is σ², and

\sigma=\sqrt{\frac{1}{N}\sum_i (x_i-\mu)^2}\quad(\mathrm{population\ }\sigma)
\mathrm{MAD}=\mathrm{median}\left(|x_i-\mathrm{median}(x)|\right)\,,\qquad \mathrm{IQR}=p_{75}-p_{25}

MAD is the robust scale estimator the viewer reports because, unlike StdDev, it is almost unaffected by bright stars and hot pixels. For Gaussian noise it relates to the standard deviation by the familiar constant:

\sigma_{\mathrm{robust}}\approx 1.4826\cdot\mathrm{MAD}

So a background's MAD × 1.4826 is a clean read of its noise sigma, free of the star-field inflation that corrupts a plain StdDev.

2.5 Clipping detection

Low- and high-clipped pixels are counted against fixed thresholds (eps = 1e-6):

\mathrm{Low}=\#\{x_i\leq\epsilon\}\,/\,N\,,\qquad \mathrm{High}=\#\{x_i\geq s-\epsilon\}\,/\,N

where s is the normalized sensor-saturation level set by the “?” control:

s=\frac{\mathrm{ADU}_{\mathrm{sat}}}{\mathrm{ADU}_{\max}}\qquad(\mathrm{default}\;s=1.0)

Setting s below 1.0 lets the High-Clipped warning fire at your sensor's real full-well level even if the stored data never quite reaches 1.0. The cell background escalates with the clipped fraction: ≥ 0.01% mild, ≥ 0.1% medium, ≥ 1% strong.

2.6 More Stats

The detailed table adds, per channel: Mean, Variance, MAD, IQR, and the percentile set p ∈ {0.1, 1, 5, 25, 50, 75, 95, 99, 99.9} (numpy.percentile). For very large images (over 20 million pixels per channel) the detailed stats are computed on a fixed random subsample of 5 million pixels (seeded for repeatability) so the dialog stays responsive; the main-panel stats and the histogram itself always use the full array.

2.7 Notes