|
3 | 3 | Extracts colors from a persistent stream binary |
4 | 4 | """ |
5 | 5 |
|
| 6 | +import math |
| 7 | +import itertools |
| 8 | +import operator |
| 9 | + |
6 | 10 | from .color_lut import COLOR_LUT |
7 | 11 |
|
8 | 12 |
|
@@ -71,6 +75,31 @@ def cielab_to_xyz(l_value, a, b): |
71 | 75 | return xr * Xr, yr * Yr, zr * Zr |
72 | 76 |
|
73 | 77 |
|
| 78 | +def cielab_to_xyz2(l_value, a, b, illuminants): |
| 79 | + """Translate lab color to xyz, CIM version""" |
| 80 | + |
| 81 | + fy = (l_value + 16) / 116.0 |
| 82 | + fz = fy - (b / 200.0) |
| 83 | + fx = a / 500.0 + fy |
| 84 | + |
| 85 | + e = 216 / 24389 # 0.008856 |
| 86 | + k = 24389 / 27 # 903.3 |
| 87 | + if fx**3 > e: |
| 88 | + xr = fx**3 |
| 89 | + else: |
| 90 | + xr = (116 * fx - 16) / k |
| 91 | + if l_value > k * e: |
| 92 | + yr = fy**3 |
| 93 | + else: |
| 94 | + yr = l_value / k |
| 95 | + if fz**3 > e: |
| 96 | + zr = fz**3 |
| 97 | + else: |
| 98 | + zr = (116 * fz - 16) / k |
| 99 | + |
| 100 | + return xr * illuminants[0], yr * illuminants[1], zr * illuminants[2] |
| 101 | + |
| 102 | + |
74 | 103 | def scale_and_round(r, g, b): |
75 | 104 | """Scale to 0-255 and round valued. The algorithm seems to be extremely |
76 | 105 | precise and equivalent to what is done inside Esri apps, except for |
@@ -132,3 +161,151 @@ def cielab_to_rgb(l_value, a, b): |
132 | 161 |
|
133 | 162 | # lab value not present in lookup table, use standard conversion formula |
134 | 163 | return scale_and_round(*apply_gamma(*xyz_to_rgb(*cielab_to_xyz(l_value, a, b)))) |
| 164 | + |
| 165 | + |
| 166 | +def matrix_multiply_3(m, v): |
| 167 | + """ |
| 168 | + matrix multiply vector by inner production. |
| 169 | + """ |
| 170 | + return [ |
| 171 | + [sum(a * b for a, b in zip(X_row, Y_col)) for Y_col in zip(*v)] for X_row in m |
| 172 | + ] |
| 173 | + |
| 174 | + |
| 175 | +def dot(x, y): |
| 176 | + """ |
| 177 | + Matrix dot product |
| 178 | + """ |
| 179 | + assert len(x) == len(y) |
| 180 | + return sum(itertools.starmap(operator.mul, zip(x, y))) |
| 181 | + |
| 182 | + |
| 183 | +def matrix_vector_product(m, v): |
| 184 | + """ |
| 185 | + Matrix vector product |
| 186 | + """ |
| 187 | + return [dot(row, v) for row in m] |
| 188 | + |
| 189 | + |
| 190 | +def matrix_invert_3x3(a): |
| 191 | + """ |
| 192 | + Inverts a 3x3 matrix |
| 193 | + """ |
| 194 | + c = [ |
| 195 | + [ |
| 196 | + a[1][1] * a[2][2] - a[1][2] * a[2][1], |
| 197 | + -1 * (a[0][1] * a[2][2] - a[0][2] * a[2][1]), |
| 198 | + a[0][1] * a[1][2] - a[0][2] * a[1][1], |
| 199 | + ], |
| 200 | + [ |
| 201 | + -1 * (a[1][0] * a[2][2] - a[1][2] * a[2][0]), |
| 202 | + a[0][0] * a[2][2] - a[0][2] * a[2][0], |
| 203 | + -1 * (a[0][0] * a[1][2] - a[0][2] * a[1][0]), |
| 204 | + ], |
| 205 | + [ |
| 206 | + a[1][0] * a[2][1] - a[1][1] * a[2][0], |
| 207 | + -1 * (a[0][0] * a[2][1] - a[0][1] * a[2][0]), |
| 208 | + a[0][0] * a[1][1] - a[0][1] * a[1][0], |
| 209 | + ], |
| 210 | + ] |
| 211 | + |
| 212 | + b = a[0][0] * c[0][0] + a[0][1] * c[1][0] + a[0][2] * c[2][0] |
| 213 | + |
| 214 | + for d in range(3): |
| 215 | + for e in range(3): |
| 216 | + c[d][e] = c[d][e] / b |
| 217 | + |
| 218 | + return c |
| 219 | + |
| 220 | + |
| 221 | +def bradford_transform(a, b): |
| 222 | + c = [ |
| 223 | + [0.8951, 0.2664, -0.1614], |
| 224 | + [-0.7502, 1.7135, 0.0367], |
| 225 | + [0.0389, -0.0685, 1.0296], |
| 226 | + ] |
| 227 | + f = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] |
| 228 | + |
| 229 | + d = matrix_vector_product(c, a) |
| 230 | + e = matrix_vector_product(c, b) |
| 231 | + |
| 232 | + for g in range(3): |
| 233 | + f[g][g] = e[g] / d[g] |
| 234 | + |
| 235 | + return matrix_multiply_3(matrix_multiply_3(matrix_invert_3x3(c), f), c) |
| 236 | + |
| 237 | + |
| 238 | +def xyz_chromaticity(a): |
| 239 | + return [a[0] / (a[0] + a[1] + a[2]), a[1] / (a[0] + a[1] + a[2])] |
| 240 | + |
| 241 | + |
| 242 | +def rgb_matrix(a, b, c, d): |
| 243 | + e = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] |
| 244 | + f = [ |
| 245 | + [a[0] / a[1], b[0] / b[1], c[0] / c[1]], |
| 246 | + [1, 1, 1], |
| 247 | + [(1 - a[0] - a[1]) / a[1], (1 - b[0] - b[1]) / b[1], (1 - c[0] - c[1]) / c[1]], |
| 248 | + ] |
| 249 | + g = [d[0] / d[1], 1, (1 - d[0] - d[1]) / d[1]] |
| 250 | + |
| 251 | + j = matrix_vector_product(matrix_invert_3x3(f), g) |
| 252 | + for h in range(3): |
| 253 | + for i in range(3): |
| 254 | + e[h][i] = j[i] * f[h][i] |
| 255 | + return e |
| 256 | + |
| 257 | + |
| 258 | +def cielab_to_rgb2(l_value, a, b): |
| 259 | + illuminants = [0.9672, 1, 0.81427] |
| 260 | + |
| 261 | + x, y, z = cielab_to_xyz2(l_value, a, b, illuminants) |
| 262 | + |
| 263 | + b = x > illuminants[0] or y > illuminants[1] or z > illuminants[2] |
| 264 | + |
| 265 | + # bc = bradford_transform(illuminants,illuminants) |
| 266 | + |
| 267 | + bt = [ |
| 268 | + [0.9522842143556952, -0.025006988135028507, 0.06684232012747596], |
| 269 | + [-0.030901431979861445, 1.0117439273544555, 0.02228245871328488], |
| 270 | + [0.012942977697302113, -0.021511192838354815, 1.348229389280612], |
| 271 | + ] |
| 272 | + |
| 273 | + x, y, z = matrix_vector_product(bt, [x, y, z]) |
| 274 | + |
| 275 | + if not b: |
| 276 | + if x > illuminants[0]: |
| 277 | + x = illuminants[0] |
| 278 | + if y > illuminants[1]: |
| 279 | + y = illuminants[1] |
| 280 | + if z > illuminants[2]: |
| 281 | + z = illuminants[2] |
| 282 | + |
| 283 | + h = (x, y, z) |
| 284 | + |
| 285 | + i = matrix_invert_3x3( |
| 286 | + rgb_matrix( |
| 287 | + [0.64, 0.33], |
| 288 | + [0.3, 0.6], |
| 289 | + [0.15, 0.06], |
| 290 | + xyz_chromaticity([0.95047, 1, 1.08883]), |
| 291 | + ) |
| 292 | + ) |
| 293 | + |
| 294 | + r, g, b = matrix_vector_product(i, h) |
| 295 | + i = [r, g, b] |
| 296 | + |
| 297 | + # convert to srgb |
| 298 | + for h in range(3): |
| 299 | + if i[h] > 0.0031308: |
| 300 | + i[h] = 1.055 * (i[h] ** (1 / 2.4)) - 0.055 |
| 301 | + else: |
| 302 | + i[h] = 12.92 * i[h] |
| 303 | + i[h] = 255 * i[h] + 0.5 |
| 304 | + if i[h] < 0: |
| 305 | + i[h] = 0 |
| 306 | + elif i[h] > 255: |
| 307 | + i[h] = 255 |
| 308 | + |
| 309 | + i[h] = math.floor(i[h]) |
| 310 | + |
| 311 | + return i |
0 commit comments