astroclr
Astroclr allows perceived colours of light sources to be calculated from the physical spectra that emit them, and conversions between various colour spaces. This allows spectra radiated from (for example) stars to be converted into colours that can be replicated on a computer display.
Astroclr is heavily inspired by Colorpy by Mark Kness, which was an invaluable resource for me when calculating colours of white dwarfs, and developing my own interest in colour science. While Colorpy is well documented and referenced, it is no longer actively maintained, and requires several tweaks for compatibility with python3. Astroclr was written with the express aim of making colour calculations from spectra as simple as possible, usng a modern python interface, and with a particular focus on observational astronomy.
Features
- A modern interface with a variety of colour classes and methods to convert
between them (all supporting numpy arrays for multiple objects or images)
- XYZ_Color
- RGB_Color
- IRGB_Color
- UCS_Color
- LUV_Color
- LAB_Color
- A bespoke Spectrum class with easy conversion to XYZ colours
- XYZ integrations are performed trapezoidally, so wavelengths can have arbitrary spacing.
- Works with astropy units for calculating luminance, illuminance, and visual magnitudes
- Easy generation of black body spectra
- Support for a variety of RGB colour spaces
- sRGB
- Rec709
- Rec2020/2100
- DCI P3
- A suite of example scripts for interesting things an astronomer might want to do, and demonstrations of various colour science concepts
Note: All documentation uses the British spelling of "colour" (since I am British). For code, however, I have opted to use "color", for consistency with Colorpy, and other similar software (and to save on typing). The module name was chosen to be neutral towards spelling.
Installation:
pip install astroclr
Examples
Calculating colours with astroclr has been made as easy as possible, with the basic recipe looking like:
- create a Spectrum object
- convert to the CIE 1931 XYZ colour space
- convert from XYZ to a linear RGB colour space (sRGB by default)
- convert linear RGB to the non-linear display RGB (IRGB) space by applying a transfer function
Lets say we want to determine the sRGB colour of a 10,000 K black body (something like the A-type star Sirius A) as a hexstring. In Astroclr we can follow the above recipe as follows:
>>> from astropy import units as u
>>> from astroclr import Spectrum, RGB_Color
>>> lam = np.arange(300, 900, 0.1) * u.nm
>>> S = Spectrum.BlackBody(temperature=10000*u.K, wavelength=lam)
>>> XYZ = S.to_XYZ()
>>> RGB = RGB_Color.from_XYZ(XYZ, brightest=True)
>>> IRGB = RGB.to_IRGB()
>>> print(IRGB.to_string())
'#CDD9FF'
The brightest keyword, ensures the brightest RGB channel is scaled to 1.0, so
observed spectra (typically faint) can be shown as bright as possible, and
bright spectra (such as models) can be displayed without colour clipping. The
above code can be actually be performed even more succinctly:
>>> S = Spectrum.BlackBody(temperature=10000*u.K, wavelength=lam)
>>> print(S.to_IRGB_string(brightest=True))
'#CDD9FF'
You may have noticed that most conversion methods are prefixed with 'to', but
to convert to RGB we need to use a 'from' method. This is because RGB colour
spaces (the default is sRGB) are defined in terms of XYZ chromaticities (see
below), and so all colour spaces are defined in terms of the more fundamental
XYZ_Color. This can be seen by inspecting the sRGB colour space:
>>> from astroclr import sRGB
>>> print(sRGB)
RGB_ColorSpace(name='sRGB', R=XYZ_Color(0.64, 0.33), G=XYZ_Color(0.3, 0.6), ...)
If you have your own spectrum, calculating colours is just as simple:
>>> wavelength, flux = np.loadtxt("my_spectrum.dat", unpack=True)
>>> wavelength *= u.AA
>>> flux *= u.erg / u.s / u.cm**2 / u.AA
>>> S = Spectrum(wavelength, flux)
>>> print(S.to_IRGB_string(brightest=True))
'#DDFFB8'
Note that fluxes must be in terms of spectral radiance/irradiance (per unit
wavelength), i.e. equivalent units to W/m²/nm/sr or W/m²/nm, which is necessary
for calculating the luminance/illuminance using the to_luminance() method. If
no units are provided, default units of nm and W/m²/nm/sr are assumed for
wavelengths and fluxes.
Colour science basics
If we have a spectrum $F_\lambda(\lambda)$ and want to calculate its displayable RGB colour. Mathematically we do something akin to synthetic photometry in astronomy:
X = \int F_\lambda(\lambda) \bar{x}(\lambda) \,\mathrm{d}\lambda \qquad
Y = \int F_\lambda(\lambda) \bar{y}(\lambda) \,\mathrm{d}\lambda \qquad
Z = \int F_\lambda(\lambda) \bar{z}(\lambda) \,\mathrm{d}\lambda
where $\bar{x}(\lambda)$ etc, are the CIE 1931 colour matching functions, and $X,Y,Z$ are the integrated fluxes in the XYZ colour space. But what if we want to consider the colour independent of its brightness/intensity/luminance? Then we can normalise the $X, Y, Z$ by scaling by their sum:
x = \frac{X}{X+Y+Z} \qquad y = \frac{Y}{X+Y+Z} \qquad z = \frac{Z}{X+Y+Z}
Having lost a degree of freedom (the intensity), $x$ and $y$ now uniquely
define the brightness-independent component of colour and is called the
chromaticity. The CIE 1931 chromaticity diagram is shown below, where the
shark-fin shape corresponds to monochromactic colours and bounds all perceivable
colours, the triangle shows the sRGB gamut (colours outside the triangle are
approximated by clipping negative values), and the black curve starting in the red
corner is the blackbody locus (ticks from 2000K to 10000K in 1000K steps).
The sRGB colour space specifies the chromaticities of the red/green/blue primaries (and also the white point), which uniquely define a linear transformation between XYZ and sRGB.
\left(\begin{array}{c}
R \\
G \\
B
\end{array}\right) =
\left(\begin{array}{rrr}
3.2410 & -1.5374 & -0.4986 \\
-0.9692 & 1.8760 & 0.0416 \\
0.0556 & -0.2040 & 1.0570
\end{array}\right) \cdot
\left(\begin{array}{c}
X \\
Y \\
Z
\end{array}\right)
These give linear intensities in the R/G/B channels in the range 0 to 1, where (R,G,B)=(1,1,1) represents full white. To convert these to display RGB values (IRGB in astroclr), we cannot simply multiply by 255 and round. As the display scale is non-linear. We therefore have to apply an opto-electrical transfer function (OETF). In some colour-spaces this is just a power-law (gamma curve), though in sRGB a piecewise function is used that is linear at low-brightness and a shifted plus scaled power-law at higher brightness, but closely resembles a power-law with exponent 1/2.2. After applying a OETF, RGB values can be multiplied by 255 and rounded to determine the 8-bit per channel display values.
Metadata
Release files for astroclr 1.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| astroclr-1.0.2.tar.gz | 2.8 MB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| astroclr-1.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.4 MB
Release files / astroclr-1.0.2.tar.gz
| Download URL | astroclr-1.0.2.tar.gz |
|---|---|
| Size | 2.8 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e3e2b8349265e554ddf3deb13e8b6d307a408cc03eee2b33aeffdcb83bf3cad9
|
|
BLAKE2b-256 checksum How to use checksums |
f21baea6dd1a8cadd1c3761a430106bbfcbb652172f8961d5ad362e829267db8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.9
|
Release files / astroclr-1.0.2-py3-none-any.whl
| Download URL | astroclr-1.0.2-py3-none-any.whl |
|---|---|
| Size | 547.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1601aec3bf7efacf0d229fd7316ca02bd6c17ef34da94ae62492ea68fef05611
|
|
BLAKE2b-256 checksum How to use checksums |
a05aa199407049c5361091d3c9e843677c6c5d6b06bba9399233faac4867bfb5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.9
|
Security for astroclr 1.0.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.