Created
December 30, 2012 14:58
-
-
Save dbr/4413132 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""Inspired by https://github.com/timbowman/blackbody | |
..this creates a Nuke ColorLookup node, which maps from Kelvin to xyY values, | |
using code from https://github.com/dbr/colourstuff | |
""" | |
def cie1931_standard_observer_rawdata(two_degree = False, ten_degree = False): | |
"""Returns dictionary with three keys: x, y and z. Each key contains | |
a dictionary mapping wavelength to value | |
Created using the from_txt_to_python function | |
""" | |
if not any([two_degree, ten_degree]) or all([two_degree, ten_degree]): | |
raise ValueError("Specify either two_degree or ten_degree") | |
cie1931_standard_observer_2deg = { | |
'x': { | |
380: 1368, | |
385: 2236, | |
390: 4243, | |
395: 7650, | |
400: 14310, | |
405: 23190, | |
410: 43510, | |
415: 77630, | |
420: 134380, | |
425: 214770, | |
430: 283900, | |
435: 328500, | |
440: 348280, | |
445: 348060, | |
450: 336200, | |
455: 318700, | |
460: 290800, | |
465: 251100, | |
470: 195360, | |
475: 142100, | |
480: 95640, | |
485: 57950, | |
490: 32010, | |
495: 14700, | |
500: 4900, | |
505: 2400, | |
510: 9300, | |
515: 29100, | |
520: 63270, | |
525: 109600, | |
530: 165500, | |
535: 225750, | |
540: 290400, | |
545: 359700, | |
550: 433450, | |
555: 512050, | |
560: 594500, | |
565: 678400, | |
570: 762100, | |
575: 842500, | |
580: 916300, | |
585: 978600, | |
590: 1026300, | |
595: 1056700, | |
600: 1062200, | |
605: 1045600, | |
610: 1002600, | |
615: 938400, | |
620: 854450, | |
625: 751400, | |
630: 642400, | |
635: 541900, | |
640: 447900, | |
645: 360800, | |
650: 283500, | |
655: 218700, | |
660: 164900, | |
665: 121200, | |
670: 87400, | |
675: 63600, | |
680: 46770, | |
685: 32900, | |
690: 22700, | |
695: 15840, | |
700: 11359, | |
705: 8111, | |
710: 5790, | |
715: 4109, | |
720: 2899, | |
725: 2049, | |
730: 1440, | |
735: 1000, | |
740: 690, | |
745: 476, | |
750: 332, | |
755: 235, | |
760: 166, | |
765: 117, | |
770: 83, | |
775: 59, | |
780: 42, | |
}, | |
'y': { | |
380: 39, | |
385: 64, | |
390: 120, | |
395: 217, | |
400: 396, | |
405: 640, | |
410: 1210, | |
415: 2180, | |
420: 4000, | |
425: 7300, | |
430: 11600, | |
435: 16840, | |
440: 23000, | |
445: 29800, | |
450: 38000, | |
455: 48000, | |
460: 60000, | |
465: 73900, | |
470: 90980, | |
475: 112600, | |
480: 139020, | |
485: 169300, | |
490: 208020, | |
495: 258600, | |
500: 323000, | |
505: 407300, | |
510: 503000, | |
515: 608200, | |
520: 710000, | |
525: 793200, | |
530: 862000, | |
535: 914850, | |
540: 954000, | |
545: 980300, | |
550: 994950, | |
555: 1000000, | |
560: 995000, | |
565: 978600, | |
570: 952000, | |
575: 915400, | |
580: 870000, | |
585: 816300, | |
590: 757000, | |
595: 694900, | |
600: 631000, | |
605: 566800, | |
610: 503000, | |
615: 441200, | |
620: 381000, | |
625: 321000, | |
630: 265000, | |
635: 217000, | |
640: 175000, | |
645: 138200, | |
650: 107000, | |
655: 81600, | |
660: 61000, | |
665: 44580, | |
670: 32000, | |
675: 23200, | |
680: 17000, | |
685: 11920, | |
690: 8210, | |
695: 5723, | |
700: 4102, | |
705: 2929, | |
710: 2091, | |
715: 1484, | |
720: 1047, | |
725: 740, | |
730: 520, | |
735: 361, | |
740: 249, | |
745: 172, | |
750: 120, | |
755: 85, | |
760: 60, | |
765: 42, | |
770: 30, | |
775: 21, | |
780: 15, | |
}, | |
'z': { | |
380: 6450, | |
385: 10550, | |
390: 20050, | |
395: 36210, | |
400: 67850, | |
405: 110200, | |
410: 207400, | |
415: 371300, | |
420: 645600, | |
425: 1039050, | |
430: 1385600, | |
435: 1622960, | |
440: 1747060, | |
445: 1782600, | |
450: 1772110, | |
455: 1744100, | |
460: 1669200, | |
465: 1528100, | |
470: 1287640, | |
475: 1041900, | |
480: 812950, | |
485: 616200, | |
490: 465180, | |
495: 353300, | |
500: 272000, | |
505: 212300, | |
510: 158200, | |
515: 111700, | |
520: 78250, | |
525: 57250, | |
530: 42160, | |
535: 29840, | |
540: 20300, | |
545: 13400, | |
550: 8750, | |
555: 5750, | |
560: 3900, | |
565: 2750, | |
570: 2100, | |
575: 1800, | |
580: 1650, | |
585: 1400, | |
590: 1100, | |
595: 1000, | |
600: 800, | |
605: 600, | |
610: 340, | |
615: 240, | |
620: 190, | |
625: 100, | |
630: 50, | |
635: 30, | |
640: 20, | |
645: 10, | |
650: 0, | |
655: 0, | |
660: 0, | |
665: 0, | |
670: 0, | |
675: 0, | |
680: 0, | |
685: 0, | |
690: 0, | |
695: 0, | |
700: 0, | |
705: 0, | |
710: 0, | |
715: 0, | |
720: 0, | |
725: 0, | |
730: 0, | |
735: 0, | |
740: 0, | |
745: 0, | |
750: 0, | |
755: 0, | |
760: 0, | |
765: 0, | |
770: 0, | |
775: 0, | |
780: 0, | |
}, | |
} | |
cie1931_standard_observer_10deg = { | |
'x': { | |
380: 160, | |
385: 662, | |
390: 2362, | |
395: 7242, | |
400: 19110, | |
405: 43400, | |
410: 84736, | |
415: 140638, | |
420: 204492, | |
425: 264737, | |
430: 314679, | |
435: 357719, | |
440: 383734, | |
445: 386726, | |
450: 370702, | |
455: 342957, | |
460: 302273, | |
465: 254085, | |
470: 195618, | |
475: 132349, | |
480: 80507, | |
485: 41072, | |
490: 16172, | |
495: 5132, | |
500: 3816, | |
505: 15444, | |
510: 37465, | |
515: 71358, | |
520: 117749, | |
525: 172953, | |
530: 236491, | |
535: 304213, | |
540: 376772, | |
545: 451584, | |
550: 529826, | |
555: 616053, | |
560: 705224, | |
565: 793832, | |
570: 878655, | |
575: 951162, | |
580: 1014160, | |
585: 1074300, | |
590: 1118520, | |
595: 1134300, | |
600: 1123990, | |
605: 1089100, | |
610: 1030480, | |
615: 950740, | |
620: 856297, | |
625: 754930, | |
630: 647467, | |
635: 535110, | |
640: 431567, | |
645: 343690, | |
650: 268329, | |
655: 204300, | |
660: 152568, | |
665: 112210, | |
670: 81261, | |
675: 57930, | |
680: 40851, | |
685: 28623, | |
690: 19941, | |
695: 13842, | |
700: 9577, | |
705: 6605, | |
710: 4553, | |
715: 3145, | |
720: 2175, | |
725: 1506, | |
730: 1045, | |
735: 727, | |
740: 508, | |
745: 356, | |
750: 251, | |
755: 178, | |
760: 126, | |
765: 90, | |
770: 65, | |
775: 46, | |
780: 33, | |
}, | |
'y': { | |
380: 17, | |
385: 72, | |
390: 253, | |
395: 769, | |
400: 2004, | |
405: 4509, | |
410: 8756, | |
415: 14456, | |
420: 21391, | |
425: 29497, | |
430: 38676, | |
435: 49602, | |
440: 62077, | |
445: 74704, | |
450: 89456, | |
455: 106256, | |
460: 128201, | |
465: 152761, | |
470: 185190, | |
475: 219940, | |
480: 253589, | |
485: 297665, | |
490: 339133, | |
495: 395379, | |
500: 460777, | |
505: 531360, | |
510: 606741, | |
515: 685660, | |
520: 761757, | |
525: 823330, | |
530: 875211, | |
535: 923810, | |
540: 961988, | |
545: 982200, | |
550: 991761, | |
555: 999110, | |
560: 997340, | |
565: 982380, | |
570: 955552, | |
575: 915175, | |
580: 868934, | |
585: 825623, | |
590: 777405, | |
595: 720353, | |
600: 658341, | |
605: 593878, | |
610: 527963, | |
615: 461834, | |
620: 398057, | |
625: 339554, | |
630: 283493, | |
635: 228254, | |
640: 179828, | |
645: 140211, | |
650: 107633, | |
655: 81187, | |
660: 60281, | |
665: 44096, | |
670: 31800, | |
675: 22602, | |
680: 15905, | |
685: 11130, | |
690: 7749, | |
695: 5375, | |
700: 3718, | |
705: 2565, | |
710: 1768, | |
715: 1222, | |
720: 846, | |
725: 586, | |
730: 407, | |
735: 284, | |
740: 199, | |
745: 140, | |
750: 98, | |
755: 70, | |
760: 50, | |
765: 36, | |
770: 25, | |
775: 18, | |
780: 13, | |
}, | |
'z': { | |
380: 705, | |
385: 2928, | |
390: 10482, | |
395: 32344, | |
400: 86011, | |
405: 197120, | |
410: 389366, | |
415: 656760, | |
420: 972542, | |
425: 1282500, | |
430: 1553480, | |
435: 1798500, | |
440: 1967280, | |
445: 2027300, | |
450: 1994800, | |
455: 1900700, | |
460: 1745370, | |
465: 1554900, | |
470: 1317560, | |
475: 1030200, | |
480: 772125, | |
485: 570060, | |
490: 415254, | |
495: 302356, | |
500: 218502, | |
505: 159249, | |
510: 112044, | |
515: 82248, | |
520: 60709, | |
525: 43050, | |
530: 30451, | |
535: 20584, | |
540: 13676, | |
545: 7918, | |
550: 3988, | |
555: 1091, | |
560: 0, | |
565: 0, | |
570: 0, | |
575: 0, | |
580: 0, | |
585: 0, | |
590: 0, | |
595: 0, | |
600: 0, | |
605: 0, | |
610: 0, | |
615: 0, | |
620: 0, | |
625: 0, | |
630: 0, | |
635: 0, | |
640: 0, | |
645: 0, | |
650: 0, | |
655: 0, | |
660: 0, | |
665: 0, | |
670: 0, | |
675: 0, | |
680: 0, | |
685: 0, | |
690: 0, | |
695: 0, | |
700: 0, | |
705: 0, | |
710: 0, | |
715: 0, | |
720: 0, | |
725: 0, | |
730: 0, | |
735: 0, | |
740: 0, | |
745: 0, | |
750: 0, | |
755: 0, | |
760: 0, | |
765: 0, | |
770: 0, | |
775: 0, | |
780: 0, | |
}, | |
} | |
if two_degree: | |
return cie1931_standard_observer_2deg | |
else: | |
return cie1931_standard_observer_10deg | |
def get_colour_matching_functions(two_degree = False, ten_degree = False): | |
"""Get the three colour matching functions, based of the CIE 1931 Standard | |
Colorimetric Observer (either the 2 or 10 degree observers) | |
Based on the 5nm tabulated data, linearly interpolates between samples. | |
Returned as a tuple of three callables, which take a wavelength in | |
nanometres, and return the reading. | |
""" | |
# Load the raw data | |
data = cie1931_standard_observer_rawdata(two_degree = two_degree, ten_degree = ten_degree) | |
# The outer function defines the channel, to prevent duplicating the | |
# function three times. | |
def getfunc(channel): | |
# The inner function uses the channel variable, and does the | |
# actual interpolation | |
def colour_matcher(value): | |
def lerp(a, b, mix): | |
return b*mix + a*(1-mix) | |
samples = sorted(data[channel]) | |
if value < min(samples): | |
raise ValueError("Value %s below minimum sample: %s" % (value, min(samples))) | |
if value > max(samples): | |
raise ValueError("Value %s above minimum sample: %s" % (value, max(samples))) | |
deltas = [abs(a - value) for a in samples] | |
smallest = deltas.index(min(deltas)) | |
ka, kb = samples[smallest-1], samples[smallest] | |
va, vb = data[channel][ka], data[channel][kb] | |
interpolant = (float(value) - ka) / (kb - ka) | |
return lerp(va, vb, interpolant) | |
colour_matcher.__doc__ = "Colour matching function for %r" % channel | |
return colour_matcher | |
return (getfunc("x"), getfunc("y"), getfunc("z")) | |
def planckian_locus_approx(T): | |
"""http://en.wikipedia.org/wiki/Planckian_locus#Approximation | |
""" | |
T = float(T) | |
if 1667 <= T <= 4000: | |
x = -0.2661239 * (10**9 / T**3) - 0.2343580 * (10**6/T**2) + 0.8776956 * (10**3/T) + 0.179910 | |
elif 4000 <= T <= 25000: | |
x = -3.0258469 * (10**9 / T**3) + 2.107379 * (10**6/T**2) + 0.2226347*(10**3/T) + 0.240390 | |
else: | |
raise ValueError("T out of range (should be between 1667K and 25000K)") | |
if 1667 <= T <= 2222: | |
y = -1.1063814 * x**3 - 1.34811020*x**2 + 2.18555832*x - 0.20219683 | |
elif 2222 <= T <= 4000: | |
y = -0.9549476*x**3 - 1.37418593*x**2 + 2.09137015*x - 0.16748867 | |
elif 4000 <= T <= 25000: | |
y = 3.0817580*x**3 - 5.87338670*x**2 + 3.75112997*x - 0.37001483 | |
else: | |
raise ValueError("T out of range (should be between 1667K and 25000)") | |
return (x, y) | |
def plancks_law(wavelen, T): | |
"""wavelen is in nanometres, and T is in Kelvin | |
$I'(\lambda,T) =\frac{2 hc^2}{\lambda^5}\frac{1}{ e^{\frac{hc}{\lambda kT}}-1}$ | |
http://en.wikipedia.org/wiki/Planck%27s_law | |
""" | |
#FIXME: Needs test cases, probably right, but maybe not | |
from math import exp, pi | |
nm = 10**-9 # nm to metres | |
h = 6.6260689633*10**-34 # Planck constant | |
k = 1.380650424*10**-23 # Boltzmann constant | |
c = 299792458 # m/s (speed of light in a vacuum) | |
c = 2.99792*10**8 | |
# Left-hand chunk of expression (that's the official maths | |
# terminology, I'm sure) | |
# http://www.wolframalpha.com/input/?i=%282*pi*h*c^2%29+%2F+5600nm^2 | |
p1 = (2*pi*h*(c**2)) / ((wavelen*nm)**5) | |
# Right-hand chunk of expression | |
# http://www.wolframalpha.com/input/?i=e^{%5Cfrac{%28Planck+constant%29%28speed+of+light+in+a+vacuum%29}{580nm+*+%28Boltzmann+constant%29+*+5600K}} | |
p2 = 1.0 / (exp((h*c) / (wavelen*nm * k * T)) - 1) | |
return p1 * p2 | |
def integrate(f, a, b, N): | |
dx = (b-a)/N | |
s = 0 | |
for i in range(N): | |
s += f(a+i*dx) | |
return s * dx | |
def planckian_locus(T): | |
"""XYZ colour coordinates of a theoretical incadescent black body | |
radiator at a given temperature, or something like that. | |
Can be used to calculate the chromaticity coordinates of, say, a | |
6500K light source | |
http://en.wikipedia.org/wiki/Planckian_locus | |
Argument T is in Kelvin, return value is a CIE XYZ value | |
""" | |
samples = (780-380)/5 # values every 5 nm are stored | |
X, Y, Z = get_colour_matching_functions(two_degree = True) | |
# Integrated between 380nm and 780nm, as that is the range of | |
# values contained in the colour-matching functions | |
X_T = integrate(lambda wavelen: plancks_law(wavelen, T) * X(wavelen), 380, 780, samples) | |
Y_T = integrate(lambda wavelen: plancks_law(wavelen, T) * Y(wavelen), 380, 780, samples) | |
Z_T = integrate(lambda wavelen: plancks_law(wavelen, T) * Z(wavelen), 380, 780, samples) | |
return (X_T, Y_T, Z_T) | |
node = nuke.nodes.ColorLookup() | |
for channel in range(1,4): | |
node.knob('lut').clearAnimated(channel) | |
node.knob('source').setSingleValue(True) | |
for T in range(1000, 10000, 100): | |
X,Y,Z = planckian_locus(T=T) | |
node['source'].setValue(T, 0) | |
# Input is temperature in kelvin, output is xyY | |
node['target'].setValue(X/sum((X,Y,Z)), 0) | |
node['target'].setValue(Y/sum((X,Y,Z)), 1) | |
node['target'].setValue(Y, 2) | |
node['setRGB'].execute() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment