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.
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.
| Control | What 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-Axis | Switch the intensity axis between linear and logarithmic, which spreads out the crowded low-intensity end typical of linear astro data. |
| Toggle Log Y-Axis | Plot 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 plot | Sends the clicked intensity as the pivot to the Hyperbolic Stretch tool, if it's open. |
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.
What the viewer computes, and the exact statistic definitions in the panel.
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.
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:
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.
On a log X axis, an intensity v is placed at horizontal fraction
(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.
For a channel's pixel values with mean μ and N
samples: Median is median(x), Variance is
σ², and
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:
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.
Low- and high-clipped pixels are counted against fixed thresholds
(eps = 1e-6):
where s is the normalized sensor-saturation level set by the
“?” control:
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.
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.