I'm wondering how to convert programmatically (i.e. with a programming language rather than an editor) from one colour temperature to another?

If, for example, I make the assumption (and it's a big assumption) that a white balance algorithm could take a processed image (e.g. JPEG, etc.) and place the overall temperature in a scene at 6500, how would you algorithmically "cool" or "warm" the scene to a specific temperature?

Obviously this is a common operation in an image editing program, but these typically operate on RAW images which have no processing applied (colour space manipulation or otherwise). In the case of RAW files, perhaps the individual RAW files contain sufficient information (in the file header) regarding the colour calibration of the camera sensor to make the specific temperature transformation more deterministic?

By the way, I asked this question on the regular Stack Overflow and it was suggested I may get a better answer here.

  • 1
    \$\begingroup\$ "[…] perhaps the individual RAW files contain sufficient information (in the file header) regarding the colour calibration of the camera sensor to make the specific temperature transformation more deterministic?" Yes. The example is for Canon's raw, so the location differs a bit for other brands. \$\endgroup\$ May 20, 2011 at 11:36
  • \$\begingroup\$ Thanks that was a very useful link! Though I'm still unsure of how to change from one colour temperature to another? \$\endgroup\$
    – trican
    May 20, 2011 at 12:16
  • \$\begingroup\$ Raw images are always interpreted with some instruction set of which the Exif info on white balance RGGB levels are one aspect. JPGs in turn are more or less composite images in which all the info is hardcoded and edits are destructive. I can't provide good examples, but I'd dive into ImageMagick which is a command line image editor with several program interfaces. Its -color-matrix option might get you started; see also: fmwconcepts.com/imagemagick/whitebalance/index.php \$\endgroup\$ May 20, 2011 at 13:43
  • \$\begingroup\$ The guy who answered on stack overflow answered correctly but with less detail. He suggested a matrix which is mathematically equivalent to a per channel gain of the off diagonal values are zero. \$\endgroup\$
    – agf1997
    May 14, 2015 at 17:13

3 Answers 3


You would need to do a color space white point conversion in L*a*b* space. Most color models that we normally work with, primarily RGB but often CMYK, are designed to support the limitations and requirements of physical hardware. Those models usually don't represent the "perceptual" space of color very well, though, and white point is definitely a perceptual aspect of color.

Whenever you need to apply perceptual adjustments to an image, such as color temperature or tint shifts, it is best done by converting from RGB to XYZ, and during the conversion, you can apply "reference white points" for the source and destination color space. Conversion from XYZ to Lab then gets you into a color space where you can perceptually remap the white point and all the colors such that they maintain continuity.

Color space conversions, chromatic adaptation, white point adjustments, etc. are rather complex mathematically. The more perceptually accurate you intend to be, the more complex the math usually ends up. An excellent source of information for color space conversions can be found at Bruce Lindbloom's web site. Some additional useful information can be found on Wikipedia. You might also find this information on CIE's D-Series Illuminants useful, as it contains white point specific information, calculations, and constants.

  • 1
    \$\begingroup\$ There is no need to go to a perceptual space such as CIELAB to perform a white balance for scene exposures such as those found in RAW camera data. White balance is an per channel exposure change in linear scene exposure space. Chromatic adaptation is used to adjust an image based on an assumed viewers state of adaptation to an illuminant. This is also done as a gain but in cone response space (LMS) which is why it will produce a reasonable result. \$\endgroup\$
    – agf1997
    May 14, 2015 at 16:52
  • 1
    \$\begingroup\$ It should be noted to get from RAW cameraRGB to XYZ and ultimately CIELAB one must go through a characterization matrix that has been optimized with a solver. The cameraRGB values must be properly white balanced in order for this matrix transformation to XYZ to be accurate effectively requiring the white balance to be done in linear scene exposure space. \$\endgroup\$
    – agf1997
    May 14, 2015 at 16:55
  • 1
    \$\begingroup\$ I was going to say you should put that into an answer, but I see you already did. Thanks for the insights, though! I was unaware this could be done so simply. \$\endgroup\$
    – jrista
    May 16, 2015 at 0:07

White balance is a very straight forward image processing manipulation. It's just a per channel gain in linear RGB space. JPG files are sub-optimal for doing white balance as they usually have both a tone curve and an inverse monitor Electro Optical Transform Function (EOTF) applied breaking the linear relationship with the scene luminance values. This is why it's usually done starting with RAW data. If you can get to linear RGB values either via the RAW data or the by inverting the non-linearity described above, you can white balance by applying the following.

R_out = R_in * R_gain
G_out = G_in
B_out = B_in * B_gain

Note it is convention to not apply a gain to the green channel as it very roughly relates to luminance and changing all three channels would shift the overall exposure as well.

There is usually a metadata field in the RAW files containing a set of white balance gains calculated by the camera using a built in algorithm. These algorithms are usually based on some form of color constancy illuminant estimation algorithm.

Once the gains are applied an encoding primary matrix, the target tone reproduction curve, and inverse EOTF should be applied.

  • \$\begingroup\$ I am curious how one can perform white balance like it is done with Lightroom, where you have a color temperature as well as color tint. Those two sliders align perfectly with the two planar Lab space axes, as well as with CIE-based white point adjustment formula that can be found online. Is it possible to control color temperature and tint separately with this kind of simplicity? Is that just some other calculation to determine the R_gain and B_gain? \$\endgroup\$
    – jrista
    May 16, 2015 at 0:09
  • \$\begingroup\$ @jrista The color temperature and tint values are used to define a chromaticity. That chromaticity is converted back to a set of R, G, and B gain factors used for white balance in scene exposure space. \$\endgroup\$
    – agf1997
    May 16, 2015 at 20:21

I had a great picture of a lady I took under weird, complex lighting (sodium vapor and mercury vapor lights). Great smile, perfect focus, excellent picture except for way, way off color.

I monkeyed with temp/tint for an hour in adobe Camera Raw with no good result. I wrote a program to hack the sidecar XMP file varying the temperature and then creating a symbolic link to the original NEF named parallel to the XMP file.

This is NOT nearly as fancy nor as technically correct as the above answer, but instead throws a massive amount of CPU at the problem and gives some (possibly) interesting results.

This creates a bunch of .NEF file links and a real .XMP sidecar file for each. Just copy a real nef/xmp file combo to clone to the test directory and set the min/max temp and dif_temp (Kelvins between clones). Another loop can be inserted to vary the tint value at a constant Kelvin but the files generated will be multiplied by the number of tints.

Run Photoshop file -> scripts -> image_processor, point it to this directory and it will create a real tif/jpg/whatever for each link at the given color temp.

This code is a Dirty, Ugly Hack and running it will probably melt your processor while attracting ball lightning and killer bees to the immediate vicinity. No warranty goes without saying.

Requires Perl (surely used daily) and mklink (may be present).

sub color_temp_experiment()  {
$nef = '6s-2014.1004-237695.acl.nef'; 
$xmp = '6s-2014.1004-237695.acl.xmp';  # crs:Temperature="6000"
$min_temp = 7800;
$max_temp = 9000;
$dif_temp = 200;   # Difference in temp per loop.
$xd = `cat $xmp`;  # Xmp Data;
($base = $nef) =~ s/\.nef//;  # Basename common to both nef and xmp.
$ii = -1;
for($temp = $min_temp; $temp <= $max_temp; $temp += $dif_temp)  {
    $link = $base . ".$temp" . 'k.nef';  # Abuse Kelvins with lower case.
    $xfn  = $base . ".$temp" . 'k.xmp';  # Abuse Kelvins with lower case.
    if(-f $link)  {
        print("Found FILE (s/b sym link!) $link. Next!\n");  # This prints!
    $cmd = "mklink $link $nef";
    printf("$ii) Cmd = $cmd\n");
    $sto = `$cmd`;
    unless(-f $link)  {
        print("ERROR! Sym link $link not found!\n StdOut=<<$sto>>\n\n");
    ($xt = $xd) =~ s/crs:Temperature="\d+/crs:Temperature="$temp/ms;
    open(O, ">$xfn");
    print(O "$xt");
    close O;



Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.