Craig DeForest
and 1 contributors


PDL::Transform::Color - Useful color system conversions for PDL


     ### Shrink an RGB image with proper linear interpolation:
     ### DEcode the sRGB image values, then interpolate, then ENcode sRGB
     $im = rpic("big_colorimage.jpg");
     $im2 = $im->invert(t_srgb())->match([500,500],{m=>'g'})->apply(t_srgb());


PDL::Transform::Color includes a variety of useful color conversion transformations. It can be used for simple hacks on machine-native color representations (RGB <-> HSV, etc.), for simple encoding/decoding of machine-native color representations such as sRGB, or for more sophisticated manipulation of absolute color standards including large-gamut or perceptual systems.

The color transforms in this module can be used for converting between proper color systems, for gamma-converting pixel values, or for generating pseudocolor from one or two input parameters. In addition to transforming color data between different representations, Several named "color maps" (also called "color tables") are provided.

The module uses linearized sRGB (lsRGB) as a fundamental color basis. sRGB is the standard color system used by most consumer- to mid-grade computer equipment, so casual users can use this color representation without much regard for gamuts, colorimetric standards, etc.

Most of the transform generators convert from lsRGB to various other systems. Notable simple ones are HSV (Hue, Saturation, Value), HSL (Hue, Saturation, Lightness), and CMYK (Cyan, Magenta, Yellow, blacK).

If you aren't familiar with PDL::Transform, you should read that POD now, as this is a subclass of PDL::Transform. Transforms represent and encapsulate vector transformations -- one- or two-way vector functions that may be applied, composed, or (if possible) inverted. They are created through constructor methods that often allow parametric adjustment at creation time.

If you just want to "manipulate some RGB images" and not learn about the esoterica of color representations, you can treat all the routines as working "from RGB" on the interval [0,1], and use t_srgb to import/export color images from/to "24-bit color" that your computer probably expects. If you care about the esoterica, read on.

The output transfer function for sRGB is nonlinear -- the luminance of a pixel on-screen varies somewhat faster than the square of the input value -- which is inconvenient for blending, merging, and manipulating color. Many common operations work best with a linear photometric representation. PDL::Transform::Color works with an internal model that is a floating-point linear system representing pixels as 3-vectors whose components are proportional to photometric brightness in the sRGB primary colors. This system is called "lsRGB" within the module.

Note that, in general, RGB representations are limited to a particular narrow gamut of physically accessible values. While the human eye has three dominant colorimetric input channels and hence color can be represented as a 3-vector, the human eye does not cleanly separate the spectra responsible for red, green, and blue stimuli. As a result, no trio of physical primary colors (which must have positive-definite spectra and positive-definite overall intensities) can represent every perceivable color -- even though they form a basis of color space.

But in digital representation, there is no hard limit on the values of the RGB vectors -- they can be negative or arbitrarily large. This permits representation of out-of-gamut values using negative or over-unity intensities. So floating-point lsRGB allows you to represent literally any color value that the human eye can perceive, and many that it can't. This is useful even though many such colors can't be rendered on a monitor. For example, you can change between several color representations and not be limited by the formal gamut of each representation -- only by the final export standard.

Three major output formats are supported: sRGB (standard "24-bit color" with the industry standard transfer function); bRGB (bytescaled RGB with a controllable gamma function (default 2.2, matching the average gamma value of most CRTs and calibrated flat monitors); or CMYK (direct linear inversion of the RGB values, with byte scaling). These are created by applying the transforms t_srgb, t_brgb, and t_cmyk, respectively, to an lsRGB color triplet.

The t_srgb export routine will translate represented colors in floating-point lsRGB to byte-encoded sRGB (or, if inverted, vice versa), using the correct (slightly more complicated than gamma functions) nonlinear scaling. In general, you can use !t_srgb to import existing images you may have found lying around the net; manipulate their hue, etc.; and re-export with t_srgb.

If you prefer to work with direct gamma functions or straight scaling, you can import/export from/to byte values with t_brgb instead. For example, to export a color in the CIE RGB system (different primaries than sRGB), use t_brgb() x t_ciergb.

There are also some pseudocolor transformations, which convert a single data value to normalized RGB. These transformations are t_pc for photometric (typical scientific) values and t_pcp for perceptual (typical consumer camera) values. They are described below, along with a collection of named pseudocolor maps that are supplied with the module.


Beacuse of the biophysics of the human eye, color is well represented as a 3-vector of red, green, and blue brightness values representing brightness in the long, middle, and short portions of the visible spectrum. However, the absorption/sensitivity bands overlap significantly, therefore no physical light (of any wavelength) can form a proper "primary color" (orthonormal basis element) of this space. While any vector in color space can be represented as a linear sum of three indepenent basis vectors ("primary colors"), there is no such thing as a negative intensity and therefore any tricolor representation of the color space is limited to a "gamut" that can be formed by positive linear combinations of the selected primary colors.

Some professional color representations (e.g. 5- and 7-color dye processes) expand this gamut to better match the overall spectral response of the human eye, at the cost of over-determining color values in what is fundamentally a 3-space.

RGB color representations require the specification of particular primary colors that represent particular spectral profiles. The choice of primaries depends on the technical solution being used for I/O. The most universal "standard" representation is the CIE RGB standard developed in 1931 by the Commission Internationale de l'Eclairage (CIE; International Commission on Illumination). The 1931 CIE RGB system is also called simply CIERGB by many sources. It uses primary wavelengths of 700nm (red), 546.1 nm (green), and 435.8 nm (blue).

The most universal "computer" representation is the sRGB standard defined by Anderson et al. (1996), which uses on slightly different primary colors than does the 1931 CIE RGB standard. This is because sRGB is based on the colorimetric output of color television phosphors in CRTs, while CIE RGB was developed based on easily lab-reproducible spectra.

The PDL::Transform::Color transformations are all relative to the sRGB color basis. Negative values are permitted, allowing representation of all colors -- possible or impossible.

CIE defined several other important color systems: first, an XYZ system based on nonphysical primaries X, Y, and Z that correspond to red, green, and blue, respectively. The XYZ system can represent all colors detectable to the human eye with positive-definite intensities of the "primaries": the necesary negative intensities are hidden in the formal spectrum of each of the primaries. The Y primary of this system corresponds closely to green, and is used by CIE as a proxy for overall luminance.

The CIE also separated "chrominance" and "luminance" signals, in a separate system called "xyY", which represents color as sum-normalized vectors "x=X/(X+Y+Z), "y=Y/(X+Y+Z)", and "z=Z/(X+Y+Z)". By construction, x+y+z=1, so "x" and "y" alone describe the color range of the system, and "Y" stands in for overall luminance.

A linear RGB system is specified exactly by the chrominance (CIE XYZ or xyY) coordinates of the three primaries, and a white point chrominance. The white point chrominance sets the relative scaling between the brightnesses of the primaries to achieve a color-free ("white") luminance. Different systems with the same R, G, B primary vectors can have different gains between those colors, yielding a slightly different shade of color at the R=G=B line. This "white" reference chrominance varies across systems, with the most common "white" standard being CIE's D65 spectrum based on a 6500K black body -- but CIE, in particular, specifies a large number of white standards, and some systems use none of those but instead specify CIE XYZ values for the white point.

Similarly, real RGB systems typically use dynamic range compression via a nonlinear transfer function which is most typically a "gamma function". A built-in database tracks about 15 standard named systems, so you can convert color values between them. Or you can specify your own system with a standard hash format (see get_rgb).

Provision exists for converting between different RGB systems with different primaries and different white points, by linearizing and then scaling. The most straightforward way to use this module to convert between two RGB systems (neither of which is lsRGB) is to inverse-transform one to lsRGB, then transform forward to the other. This is accomplished with the t_shift_rgb transform.

Many other representations than RGB exist to separate chromatic value from brightness. In general, these can be divided into polar coordinates that represent hue as a single value divorced from the rgb basis, and those that represent it as a combination of two values like the 'x' and 'y' of the CIE xyY space. These are all based on the Munsell and Ostwald color systems, which were worked out at about the same time as the CIE system. Both Ostwald and Munsell worked around the start of the 20th century pioneered colorimetric classification.

Ostwald worked with quasi-linear representations of chromaticity as a 2-vector independent of brightness; these representations relate to CIERGB, CIEXYZ, and related systems via simple geometric projection; the CIE xyY space is an example. The most commonly used variant of xyY is CIELAB, a perceptual color space that separates color into a perceived lightness parameter L, and separate chromaticities 'a' and 'b'. CIELAB is commonly used by graphic artists and related professions, because it is an absolute space like XYZ (so that each LAB value corresponds to a particular perceivable color), and because the Cartesian norm between vectors in LAB space is approximately proportional to perceived difference between the corresponding colors. The system is thus useful for communicating color values precisely across different groups or for developing perceptually-uniform display maps for generated data. The L, A, and B coordinates are highly nonlinear to approximately match the typical human visual system.

Other related systems include YUV, YPbPr, and YCbCr -- which are used for representing color for digital cinema and for video transmission.

Munsell developed a color system based on separating the "hue" of a color into a single value separate from both its brightness and saturation level. This system is closely related to cylindrical polar coordinates in an RGB space, with the center of the cylinder on top of the line of values corresponding to neutral shades from "black" through "grey" to "white".

Two simple Munsell-like representations that work within the gamut of a particular RGB basis are HSL and HSV. Both of these systems are loose representations that are best defined relative to a particular RGB system. They are both designed specifically to represent an entire RGB gamut with a quasi-polar coordinate system, and are based on hexagonal angle -- i.e. they are not exactly polar in nature.

HSL separates "Hue" and "Saturation" from "Lightness". Hue represents the spectral shade of the color as a direction from the central white reference line through RGB space: the R=G=B line. Saturation is a normalized chromaticity measuring fraction of the distance from the white locus to the nearest edge of the RGB gamut at a particular hue and lightness. Lightness is an approximately hue- independent measure of total intensity. Deeply objectively "saturated" colors are only accessible at L=0.5; the L=0.5 surface includes all the additive and subtractive primary colors of the RGB system. Darker colors are less-saturated shades, while brighter colors fade to pastels.

HSV is similar to HSL, but tracks only the brightest component among the RGB triplet as "Value" rather than the derived "Lightness". As a result, highly saturated HSV values have lower overall luminance than unsaturated HSV values with the same V, and the V=1 surface includes all the primary and secondary colors of the parent RGB system. This system takes advantage of the of the "Helmholtz-Kolhrausch effect" that perceived brightness increases with saturation, so V better approximates perceived brightness at a given hue and saturation, than does L.

Modern display devices generally produce physical brightnesses that are proportional not to their input signal, but to a nonlinear function of the input signal. The most common nonlinear function is a simple power law ("gamma function"): output is approximately proportional to the "gamma" power of the input. Raising a signal value to the power "1/gamma" is gamma-encoding it, and raising it to the power "gamma" is gamma-decoding it.

The sRGB 24-bit color standard specifies a slightly more complicated transfer curve, that consists of a linear segment spliced onto a horizontally-offset power law with gamma=2.4. This reduces quantization noise for very dark pxels, but approximates an overall power law with gamma=2.2. Hence, t_brgb (which supports general power law transfer functions) defaults to an output gamma of 2.2, but t_srgb yields a more accurate export transfer in typical use. The gamma value of 2.2 was selected in the early days of the television era, to approximately match the perceptual response of the human eye, and for nearly 50 years cathode-ray-tube (CRT) displays were specifically designed for a transfer gamma of 2.2 between applied voltage at the electron gun input stage and luminance (luminous energy flux) at the display screen.

Incidentally, some now-obsolete display systems (early MacOS systems and Silcon Graphics displays) operated with a gamma factor of 1.8, slightly less nonlinear than the standard. This derives from early use of checkerboard (and similar) pixelwise dithering to achieve a higher-bit-depth color palette than was otherwise possible, with early equipment. The display gamma of 2.2 interacted with direct dithering of digital values in the nonlinear space, to produce an effective gamma closer to 1.8 than 2.2.



This is a gamma correction exponent used to get physical luminance values from the represented RGB values in the source RGB space. Most color manipulation is performed in linear (gamma=1) representation -- i.e. if you specify a gamma to a conversion transform, the normalized RGB values are decoded to linear physical values before processing in the forward direction, or encoded after processing in the reverse direction.

For example, to square the normalized floating-point lsRGB values before conversion to bRGB, use t_brgb(gamma=2)>. The "gamma" option specifies that the desired brightness of the output device varies as the square of the pixel value in the stored data.

Since lsRGB is the default working space for most transforms, you don't normally need to specify gamma -- the default value of 1.0 is correct.

Contrariwise, the t_brgb export transform has a display_gamma option that specifies the gamma function for the output bytes. Therefore, t_brgb(display_gamma=2)> square-roots the data before export (so that squaring them would yield numbers proportional to the desired luminance of a display device).

The gamma option is kept for completeness, but unless you know it's what you really want, you probably don't actually want it: instead, you should consider working in a linear space and decoding/encoding the gamma of your import/export color space only as you read in or write out values. For example, generic images found on the internet are typically in the sRGB system, and can be imported to lsRGB via the !t_srgb transform or exported with t_srgb -- or other gamma-corrected 24-bit color systems can be handled directly with t_brgb and its display_gamma option.


Copyright 2017, Craig DeForest ( This module may be modified and distributed under the same terms as PDL itself. The module comes with NO WARRANTY.



    $t = t_gamma($gamma);

This is an internal generator that is used to implement the standard gamma parameter for all color transforms. It is exported as well because many casual users just want to apply a gamma curve to existing data rather than doing anything more rigorous.

In the forward direction, t_gamma applies/decodes the gamma correction indicated -- e.g. if the $gamma parameter at generation time is 2, then the forward direction squares its input, and the inverse direction takes the square root (encodes the gamma correction).

Gamma correction is implemented using a sign-tolerant approach: all values have their magnitude scaled with the power law, regardless of the sign of the value.


    $t = t_brgb();

Convert lsRGB (normalized to [0,1]) to byte-scaled RGB ( [0,255] ). By default, t_brgb prepares byte values tuned for a display gamma of 2.2, which approximates sRGB (the standard output color coding for most computer displays). The difference between t_brgb and t_srgb in this usage is that t_srgb uses the actual spliced-curve approximation specified in the sRGB standard, while t_brgb uses a simple gamma law for export.

t_brgb accepts the following options, all of which may be abbreviated:

gamma (default 1)

If set, this is a gamma-encoding value for the original lsRGB, which is decoded before the transform.

display_gamma (default 2.2)

If set, this is the gamma of the display for which the output is intended. The default compresses the brightness vector before output (taking approximately the square root). This matches the "standard gamma" applied by MacOS and Microsoft Windows displays, and approximates the sRGB standard. See also t_srgb.

clip (default 1)

If set, the output is clipped to [0,256) in the forward direction and to [0,1] in the reverse direction.

byte (default 1)

If set, the output is converted to byte type in the forward direction. This is a non-reversible operation, because precision is lost in the conversion to bytes. (The reverse transform always creates a floating point value, since lsRGB exists on the interval [0,1] and an integer type would be useless.)


Converts lsRGB (the internal floating-point base representation) to sRGB - the typical RGB encoding used by most computing devices. Since most computer terminals use sRGB, the representation's gamut is well matched to most computer monitors.

sRGB is a spliced standard, rather than having a direct gamma correction. Hence there is no way to adjust the output gamma. If you want to do that, use t_brgb instead.

t_srgb accepts the following options, all of which may be abbreviated:

gamma (default 1)

If set, this is a gamma-encoding value for the original lsRGB, which is decoded before the transform.

byte (default 1)

If set, this causes the output to be clipped to the range [0,255] and rounded to a byte type PDL ("24-bit color"). (The reverse transform always creates a floating point value, since lsRGB exists on the interval [0,1] and an integer type would be useless.)

clip (default 0)

If set, this causes output to be clipped to the range [0,255] even if the byte option is not set.

t_pc and t_pcp

These two transforms implement a general purpose pseudocolor transformation. You input a monochromatic value (zero active dims) and get out an RGB value (one active dim, size 3). Because the most common use case is to generate sRGB values, the default output is sRGB -- you have to set a flag for lsRGB output, for example if you want to produce output in some other system by composing t_pc with a color transformation.

t_pc generates pseudocolor transforms ("color maps") with a photometric interpretation of the input: the input data are considered to be proportional to some kind of measured luminance or similar physical parameter. This produces "correct" renderings of scenes captured by scientific cameras and similar instrumentation.

t_pcp generates pseudocolor transforms ("color maps") with a perceptual interpretation of the input: the input data are considered to be proportional to the *perceptual* variation desired across the display. This produces "correct" renderings of many non-luminant types of data, such as temperature, Doppler shift, frequency plots, etc.

Both t_pc and t_pcp generate transforms based on a collection of named transformations stored in an internal database (the global hash ref $PDL::Transform::Color::pc_tab). The transformations come in two basic sorts: quasi-photometric transformations, which use luminance as the dominant varying parameter; and non- photometric transformations, which use hue or saturation as the dominant varying parameter. Only the photometric transformations get modified by t_pc vs t_pcp -- for example, t_pcp('rainbow') will yield the same transform as t_pc('rainbow').

Some of the color transformations are "split" and intended for display of signed data -- for example, the dop transformation fades red-to-white-to-blue and is intended for display of Doppler or similar signals.

NOTE: t_pc and t_pcp work BACKWARDS from most of the transformations in this package: they convert FROM a data value TO sRGB or lsRGB.

There are options to adjust input gamma and the domain of the transformation (e.g. if your input data are on [0,1000] instead of [0,1]).

If you feed in no arguments at all, either t_pc or t_pcp will list a collection of named pseudocolor transformations that work, on the standard output.

Options accepted are:

gamma (default 1) - presumed encoding gamma of the input

The input is *decoded* from this gamma value. 1 treats it as linear in luminance.

lsRGB (default 0) - produce lsRGB output instead of sRGB.

(this may be abbreviated "l" for "linear")

domain - domain of the input; synonym for irange.
irange (default [0,1]) - input range of the data

Input data are by default clipped to [0,1] before application of the color map. Specifying an undefined value causes the color map to be autoscaled to the input data, e.g. ir=[0,undef]> causes the color map to be scaled from 0 to the maximum value of the input. For full autoscaling, use ir=[]>.

combination (default 0) - recombine r,g,b post facto

This option allows you to perturb maps you like by mixing up r, g, and b after all the other calculations are done. You feed in a number from 0 to 5. If it's nonzero, you get a different combination of the three primaries. You can mock this up more compactly by appending -Cn to the (possibly abbreviated) name of the table. (Replace the 'n' with a number).

For example, if you speciy the color table sepia or sepia-c0 you'll get the sepiatone color table. If you specify sepia-c5 you'll get almost the exact same color table as grepia.

You can abbreviate color table names with unique abbreviations. Tables currently accepted, and their intended uses are:

grey, gray, or mono (photometric)

Simple monochrome.

sepia, blepia, grepia, vepia, ryg - sepiatone and variants

These use color scaling to enhance contrast in a simple luminance transfer. sepia is a black-brown-white curve reminiscent of sepia ink. The others are similar, but emphasize different primary colors. The 'ryg' duplicates sepiatone, but with green highlights to increase contrast in near-saturated parts of an image.


This black-red-yellow-white is reminiscent of blackbody curves (but does not match them rigorously).

pm3d, voy

"pm3d" is the default color table for Gnuplot. It's a colorblind-friendly, highly saturated table with horrible aesthetics but good contrast throughout. "voy" is violet-orange-yellow. It's a more aesthetically pleasing colorblind- friendly map with a ton of contrast throughout the range.


deep green through blue to white

spring, summer, autumn, winter

These are reminiscent of the "seasonal" colors provided by MatLab. The "spring" is horrendous but may be useful for certain aesthetic presentations. Summer and Winter are similar to the sepia-like tables, but with different color paths. Autumn is similar to heat, but less garish.

dop, dop1, dop2, dop3

These are various presentations of signed infromation, originally intended to display Doppler shift. They are all quasi-photometric and split.


This is a violet-black-green signed fade useful for non-Doppler signed quantities. Quasi-photometric and split.


Colors of the rainbow, red through "violet" (magenta)


The full "color wheel", including the controversial magenta-to-red segment

t_cieXYZ, t_xyz

The t_cieXYZ transform (also t_xyz, which is a synonym) converts the module-native lsRGB to the CIE XYZ representation. CIE XYZ is a nonphysical RGB-style system that minimally represents every physical color it is possible for humans to perceive in steady illumination. It is related to sRGB by a linear transformation (i.e. matrix multiplication) and forms the basis of many other color systems (such as CIE xyY).

CIE XYZ values are defined in such a way that they are positive definite for all human-perceptible colors, at the cost that the primaries are nonphysical (they correspond to no possible spectral color)

t_ciexyz accepts the following options:

gamma (default 1)

This is taken to be a coded gamma value in the original lsRGB, which is decoded before conversion to the CIE XYZ system.

rgb_system (default undef)

If present, this must be either the name of an RGB system or an RGB system descriptor hash as described in t_shift_rgb. If none is specified, then the standard linearized sRGB used by the rest of the module is assumed.

use_system_gamma (default 0)

If this flag is set, and rgb_system is set also, then the RGB side of the transform is taken to be gamma-encoded with the default value for that RGB system. Unless you explicitly specify an RGB system (with a name or a hash), this flag is ignored.


Convert RGB to RG chroma with a separate intensity channel.

Note that intensity is just the average of the R, G, and B values. If you want perceptible luminance, use t_rgl or t_ycbcr instead.

t_xyy and t_xyY

Convert from sRGB to CIE xyY. The xyY system is part of the CIE 1931 color specification. Luminance is in the 2 coordinate, and chrominance x and y are in the 0 and 1 coordinates.

This is the coordinate system in which "chromaticity diagrams" are plotted. It is capable of representing every illuminant color that can be perceived by the typical human eye, and also many that can't, with positive-definite coordinates.

Most of the domain space (which runs over [0-1] in all three dimensions) is inaccessible to most displays, because RGB gamuts are generally smaller than the actual visual gamut, which in turn is a subset of the actual xyY data space.

t_cielab or t_lab

    $t = t_cielab();

Convert RGB to CIE Lab colors. Lab stands for Lightness, "a", and "b", representing the overall luminance detection and two opponent systems (a: red/green, and b:yellow/blue) in the human eye. Lab colors are approximately perceptually uniform: they're mapped using a nonlinear transformation involving cube roots. Lab has the property that Euclidean distances of equal size in the space yield approximately equal perceptual shifts in the represented color.

Lightness runs 0-100, and the a and b opponent systems run -100 to +100.

The Lab space includes the entire CIE XYZ gamut and many "impossible colors". that cannot be represented directly with physical light. Many of these "impossible colors" (also "chimeric colors") can be experienced directly using visual fatigue effects, and can be classified using Lab.

Lab is easiest to convert directly from XYZ space, so the t_lab constructor returns a compound transform of t_xyz2lab and t_xyz.


    $t = t_xyz2lab();

Converts CIE XYZ to CIE Lab.


converts rgb to cmyk in the most straightforward way (by subtracting RGB values from unity).

CMYK and other process spaces are very complicated; this transform presents only a relatively simple conversion that does not take into account ink gamut variation or many other effects.

There *is* a provision for halftone gamma correction: "htgamma", which works exactly like the rgb gamma correction but is applied to the CMYK output.


gamma (default 1)

The standard gamma affecting the RGB cube

htgamma (default 1)

A "halftone gamma" that is suitable for non-wash output processes such as halftoning. it acts on the CMYK values themselves.

byte (default 0)

If present, the CMYK side is scaled to 0-255 and converted to a byte type.

t_hsl and t_hsv

    $rgb = $hsl->invert($t_hsl());

HSL stands for Hue, Saturation, Lightness. It's not an absolute color space, simply derived from each RGB (by default, linearized sRGB). it has the same gamut as the host RGB system. The coordinates are hexagonal on the (RYGCBM) hexagon, following the nearest face of the (diagonally sliced) RGB cube.

HSL is a double-cone system, so iso-L surfaces are close to the plane perpendicular to the double-diagonal white/illuminant line R=G=B. This has the effect of reducing saturation at high lightness levels, but maintains luminosity independent of saturation. Maximum saturation occurs when S=1 and L=0.5; at higher values of L, colors grow less saturated and more pastel, so that L follows total luminosity of the output.

HSV is a stacked-cone system: iso-V surfaces are parallel to the bright faces of the RGB cube, so maximal bright saturation occurs when S=1 and V=1. This means that output luminosity drops with saturation, but due to Helmholtz-Kolrausch effect (linking saturation to apparent brightness) the *perceived* brightness is less S-dependent: V follows total *apparent brightness* of the output, though output luminosity drops with S.

You can represent out-of-gamut values in either system, by using S values greater than unity, or "illegal" V or L values.

Hue, Saturation, and (Lightness or Value) each run from 0 to 1.

By default, the hue value follows a sin**4 scaling along each side of the RYGCBM hexagon. This softens the boundaries near the edges of the RGB cube, giving a better peceptual "color-wheel" transition between hues. There is a flag to switch to the linear behavior described in, e.g., the Wikipedia article on the HSV system.

You can encode the Lightness or Value with a gamma value ("lgamma") if desired.


gamma (default 1)

Treat the base RGB as gamma-encoded (default 1 is linear)

lgamma (default 1)

Treat the L coordinate as gamma-encoded (default 1 is linear).

hsv (default 0 if called as "t_hsl", 1 if called as "t_hsv")

Sets which of the HSL/HSV transform is to be used.

hue_linear (default 0)

This flag determines how the hue ("angle") is calculated. By default, a sin**4 scaling is used along each branch of the RYGCBM hexagon, to soften the perceptual effects at the corners. If you set this flag, then the calculated "hue" is linear along each branch of the hexagon, to match (e.g.) the Wikipedia definition.


t_new_illuminant shifts a color from an old RGB system to a new one with a different white point. It accepts either a PDL containing a CIE xyY representation of the new illuminant, or a name of the new illuminant, and some options.

Because this is shifting RGB to RGB in the same representation, gamma transformations get re-encoded afterward: if you use, for example, gamma=2>, then the RGB values are squared, then transformed, then square-rooted.

Options are:

gamma (default=1)

If present, this is the gamma coefficient for the representation of both the source and destination RGB spaces.

from (default="D65")

If present, this is the xyY or name of the OLD illuminant. The default is D65, the illuminant for sRGB (and therefore lsRGB as well).

basis (default="sRGB")

If present, this needs to be either "sRGB" or "XYZ" (case insensitive). If it's sRGB, the input and output are treated as standard lsRGB coordinates. If it's XYZ, then the input and output are in CIE XYZ coordinates.

method (default="Bradford")

This can be "Bradford", "Von Kries", "XYZ", or a 3x3 matrix Ma (see


  $t = t_shift_rgb("NTSC",{from=>"sRGB"});

Shifts the primary color basis of the lsrgb TO the destination system. Most named RGB systems have an associated preferred gamma, but that is ignored by default: the RGB values are treated as if they are all linear representations. You can specify EITHER the name of the system OR the specific RGB parameters for that system.

The RGB parameters, if you specify them, need to be in the form of a hash ref. The hash keys should be the same as would be returned by PDL::Transform::Color::get_rgb. All the keys must be present, except for gamma (which is ignored).

Alternatively, you can use the name of a known system. These are listed in the documentation for PDL::Transform::Color::get_rgb.

t_shift_rgb takes several options.

gamma (default 1)

The input triplets are assumed to be encoded with this gamma function. The default assumes linear representation.

ogamma (default gamma)

The output triplets are assumed to need encoding with this gamma function.

use_system_gammas (default 0)

This overrides the settings of "gamma" and "ogamma", and encodes/decodes according to the original system.

wp_method (default undef)

This is the whitepoint shift method used to change illuminant value between systems with different whitepoints. See t_shift_illuminant for an explanation.

from (default "sRGB")

This is the RGB system to convert from, in the same format as the system to convert to (names or a hash ref as described).


     $xyy = PDL::Transform::Color::xyy_from_D($D_value)

This utility routine generates CIE xyY system colorimetric values for standard CIE D-class illuminants (e.g., D50 or D65). The illuminants are calculated from a standard formula and correspond to black body temperatures between 4,000K and 250,000K. The D value is the temperature in K divided by 100, e.g. broad daylight is D65, corresponding to 6500 Kelvin.

This is used for calculating standard reference illuminants, to convert RGB values between illuminants.

For example, sRGB uses a D65 illuminant, but many other color standards refer to a D50 illuminant.

The colorimetric values are xy only; the Y coordinate can be specified via an option, or defaults to 0.5.

This routine is mainly used by xyy_from_illuminant, which handles most of the CIE-recognized standard illuminant sources including the D's.

See t_xyy for a description of the CIE xyY absolute colorimetric system.

xyy_from_D accepts the following options:

Y - the Y value of the output xyY coordinate


     $xyy = PDL::Transform::Color::xyy_from_illuminant($name)

This utility routine generates CIE xyY system colorimetric values for all of the standard CIE illuminants. The illuminants are looked up in a table populated from the CIE publication Colorimetry, 3rd edition.

The illuminant of a system is equivalent to its white point -- it is the location in xyY absolute colorimetric space that corresponds to "white".

CIE recognizes many standard illuminants, and (as of 2017) is in the process of creating a new set -- the "L" series illuminants -- that is meant to represent LED lighting.

Proper treatment of an illuminant requires a full spectral representation, which the CIE specifies for each illuminant. Analysis of that spectrum is a major part of what CIE calls "Color rendering index (CRI)" for a particular light source. PDL::Transform::Color is a strictly tri-coordinate system and does not handle the nuances of spectral effects on CRI. In effect, all illuminants are treated as having a CRI of unity (perfect).

Illuminants that are understood are:

  • a 3-PDL in CIE xyY coordinates

  • a CIE standard name

The CIE names are:

A - a gas-filled tungsten filament lamp at 2856K
B - not supported (deprecated by CIE)
C - early daylight simulant, replaced by the D[n] sources
D[n] - Blackbody radiation at 100[n] Kelvin (e.g. D65)
F[n] - Fluorescent lights of various types (n=1-12 or 3.1-3.15)
HP[n] - High Pressure discharge lamps (n=1-5)
L[n] - LED lighting (not yet supported)


    my $rgb_hash = get_rgb( $name );

PDL::Transform::Color::get_rgb is an internal routine that retrieves a set of RGB primary colors from an internal database. There are several named RGB systems, with different primary colors for each. The primary colors are represented as CIE xyY values in a returned hash ref.

The return value is a hash ref with the following fields:

gamma - the specified gamma of that RGB system (or 2.2, for sRGB)
w_name - the name of the illuminant / white-point for that system
w - the xyY value of the illuminant / white-point for that system
r - the xyY value of the red primary color at unit intensity
g - the xyY value of the green primary color at unit intensity
b - the xyY value of the blue primary color at unit intensity

Recognized RGB system names are:

Adobe - Adobe's 1998 RGB, intended to encompass nearly all of the CMYK gamut (gamma=2.2, white=D65)
Apple - Apple's display standard from c. 1990 - c. 2010 (gamma=1.8, white=D65)
Best - Wide-gamut RGB developed by Don Hutcheson ( (gamma=2.2, white=D50)
Beta - Bruce Lindbloom's optimized ultra-wide-gamut RGB (gamma=2.2, white=D50)
Bruce - Bruce Fraser's conservative-gamut RGB space for 8-bit editing (gamma=2.2, white=D65)
BT 601 - ITU-R standard BT.601 (used for MPEG & SDTV) (gamma=2.2, white=D65)
BT 709 - ITU-R standard BT.709 (used for HDTV) (gamma=2.2, white=D65)
CIE - CIE 1931 calibrated color space (based on physical emission lines) (gamma=2.2, white=E)
ColorMatch - quasi-standard from c.1990 -- matches Radius Pressview CRT monitors. (gamma=1.8, white=D50)
Don 4 - wide-gamut D50 working space gets the Ektachrome color gamut (gamma=2.2, white=D50)
ECI v2 - RGB standard from the European Color Initiative (gamma=1, white=D50)
Ekta PS5 - developed by Joseph Holms ( for scanned Ektachrome slides (gamma=2.2, white=D50)
NTSC - Never The Same Color (U.S. analog TV standard) (gamma=2.2, white=C)
PAL - Pictures Always Lovely (U.K. analog TV standard) (gamma = 2.2, white=D65)
ProPhoto - Wide gamut from Kodak, designed for photo output. (gamma=1.8, white=D60)
ROMM - Synonym for ProPhoto (gamma=1.8, white=D60)
SECAM - Systeme Electronique Contre les AMericains (French analog TV standard) (gamma=2.2, white=D65)
SMPTE-C - Soc. Motion Pict. & TV Engineers (current U.S. TV standard) (gamma=2.2, white=D65)
sRGB - Standard for consumer computer monitors (gamma~2.2, white=D65)
wgRGB - Wide Gamut RGB (gamma=2.2, white=D50)