1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
import numpy as np, os, multiprocessing
from colour import *
from colour.models import eotf_inverse_sRGB
from gsoc_common import D65, D65_xy, jakob_hanika, model_sd, plot_comparison
# Resolution of the discretization cubes
# Both should be 64, but this code is a bit too slow at the moment
CHROMA_STEPS = 8
LIGHTNESS_STEPS = 8
# Solve for a specific RGB color
def optimize_RGB(linear_RGB, ccp0):
RGB = eotf_inverse_sRGB(linear_RGB)
target = sRGB_to_XYZ(RGB, D65_xy)
ccp, error = jakob_hanika(target, D65, D65_xy, ccp0, verbose=False)
if error > 0.1:
print("WARNING: convergence failed with L=%s, starting from %s"
% (linear_RGB, ccp0))
return ccp, error
# Solve for all lightness values of a fully saturated RGB color
def optimize_chromaticity(argv):
linear_RGB, dirname = argv
# alpha's aren't spaced equally (see the article)
def smoothstep(x):
return x**2 * (3 - 2 * x)
ii = np.arange(0, LIGHTNESS_STEPS)
aa = smoothstep(smoothstep(ii / LIGHTNESS_STEPS))
ccps = np.empty((LIGHTNESS_STEPS, 3)) # FIXME: dtype?
def f(alpha, ccp0):
path = "%s/%.3f" % (dirname, alpha)
print("%s..." % path)
ccp, error = optimize_RGB(linear_RGB * alpha, ccp0)
try:
os.makedirs(os.path.dirname(path))
except:
pass
with open(path, "w") as fd:
fd.write("%s, %s" % (ccp, error))
if error > 0.1:
fd.write(" FAILED")
fd.write("\n")
return ccp, error
i_mid = LIGHTNESS_STEPS // 2
ccps[i_mid, :], _ = f(aa[i_mid], (0, 0, 0))
ccp0 = ccps[i_mid, :]
for i in range(i_mid + 1, LIGHTNESS_STEPS):
ccps[i, :], _ = f(aa[i], ccp0)
ccp0 = ccps[i_mid, :]
for i in reversed(range(0, i_mid)):
ccps[i, :], _ = f(aa[i], ccp0)
# This demo discretizes the sRGB space to three 8x8x8 cubes and tries to find
# the model parameters for all the colors. Coefficients are saved to a file
# somewhere in the 'out' directory (each color gets its own file).
# FIXME: This dumpster fire of an output format is to be replaced ASAP.
# FIXME: This takes a good while (on the order of an hour).
if __name__ == "__main__":
args = []
done = 0
total = 0
for A in np.linspace(0, 1, CHROMA_STEPS):
for B in np.linspace(0, 1, CHROMA_STEPS):
for RGB in [np.array([1, A, B]),
np.array([A, 1, B]),
np.array([A, B, 1])]:
dirname = "out/R%.3fG%.3fB%.3f" % (*RGB,)
total += 1
if os.path.exists(dirname) and \
len(os.listdir(dirname)) == LIGHTNESS_STEPS:
done += 1
continue
args.append((RGB, dirname))
print("%d/%d already done" % (done, total))
pool = multiprocessing.Pool()
pool.map(optimize_chromaticity, args)
|