Created
February 16, 2022 20:31
-
-
Save fredrik-johansson/4098b7aea7e0321ac50bb533a03515d0 to your computer and use it in GitHub Desktop.
Color functions
This file contains 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
from colorsys import hls_to_rgb, rgb_to_hls | |
import math | |
import cmath | |
CLAMP = lambda y: max(0.0, min(y, 1.0)) | |
BLEND = lambda x, y: 0.5*x + 0.5*y | |
DODGE = lambda a, b: a / (1.0 - b + 1/256.0) | |
# gimp color balance algorithm | |
def balance_channel(value, l, shadows, midtones, highlights): | |
a = 0.25 | |
b = 0.333 | |
scale = 0.7 | |
shadows *= CLAMP((l - b) / (-a) + 0.5) * scale | |
midtones *= CLAMP((l - b) / ( a) + 0.5) * CLAMP((l + b - 1.0) / (-a) + 0.5) * scale | |
highlights *= CLAMP((l + b - 1.0) / ( a) + 0.5) * scale | |
value += shadows | |
value += midtones | |
value += highlights | |
return CLAMP(value) | |
def balance(R, G, B, ra, rb, rc, ga, gb, gc, ba, bb, bc): | |
h, l, s = rgb_to_hls(R, G, B) | |
R = balance_channel(R, R, ra, rb, rc) | |
G = balance_channel(G, G, ga, gb, gc) | |
B = balance_channel(B, B, ba, bb, bc) | |
# preserve lightness | |
h2, l2, s2 = rgb_to_hls(R, G, B) | |
return hls_to_rgb(h2, l, s2) | |
blue_orange_colors = [ | |
(-1.0, 0.0, 0.0, 0.0 ), | |
(-0.95, 0.1, 0.2, 0.5 ), | |
(-0.5, 0.0, 0.5, 1.0 ), | |
(-0.05, 0.4, 0.8, 0.8 ), | |
( 0.0, 1.0, 1.0, 1.0 ), | |
( 0.05, 1.0, 0.9, 0.3 ), | |
( 0.5, 0.9, 0.5, 0.0 ), | |
( 0.95, 0.7, 0.1, 0.0 ), | |
( 1.0, 0.0, 0.0, 0.0 ), | |
( 2.0, 0.0, 0.0, 0.0 ), | |
] | |
def color_function(z, mode=0): | |
z = complex(z) | |
if cmath.isinf(z): | |
return (1.0, 1.0, 1.0) | |
if cmath.isnan(z): | |
return (0.5, 0.5, 0.5) | |
if mode == 0: | |
H = cmath.phase(z) | |
PI = math.pi | |
H = (H + PI) / (2 * PI) + 0.5 | |
H = H - math.floor(H) | |
t = abs(z) | |
if t > 1e60: | |
L = 1.0 | |
elif t < 1e-60: | |
L = 0.0 | |
else: | |
L = 1.0 - 1.0/(1.0 + t ** 0.2); | |
S = 0.8 | |
return hls_to_rgb(H, L, S) | |
if mode == 1: | |
H = cmath.phase(z) | |
PI = math.pi | |
H = H / PI | |
H = max(min(H, 1.0), -1.0) | |
i = 1 | |
while 1: | |
if blue_orange_colors[i][0] > H: | |
a = blue_orange_colors[i-1][0] | |
ra = blue_orange_colors[i-1][1] | |
ga = blue_orange_colors[i-1][2] | |
ba = blue_orange_colors[i-1][3] | |
b = blue_orange_colors[i][0] | |
rb = blue_orange_colors[i][1] | |
gb = blue_orange_colors[i][2] | |
bb = blue_orange_colors[i][3] | |
s = (H - a) / (b - a) | |
R = ra + (rb - ra) * s | |
G = ga + (gb - ga) * s | |
B = ba + (bb - ba) * s | |
return R, G, B | |
i += 1 | |
assert 2 <= mode <= 6 | |
R1, G1, B1 = color_function(z, 0) | |
R2, G2, B2 = color_function(z, 1) | |
R = BLEND(R1, CLAMP(DODGE(R1, R2))) | |
G = BLEND(G1, CLAMP(DODGE(G1, G2))) | |
B = BLEND(B1, CLAMP(DODGE(B1, B2))) | |
if mode == 3: | |
return balance(R, G, B, 0.0, -0.5, 0.2, 0.0, 0.0, -0.1, 0.0, -1.0, -0.2) | |
elif mode == 4: | |
return balance(R, G, B, 0.0, -0.5, 0.2, 0.0, 0.5, -0.1, 0.0, -0.3, -1.0) | |
elif mode == 5: | |
return balance(R, G, B, 0.0, -0.5, -1.0, 0.0, -0.1, -0.67, 0.0, -0.55, -0.12) | |
elif mode == 6: | |
return balance(R, G, B, 0.86, 0.0, 0.13, 0.57, 0.19, -0.52, 0.31, -0.30, -0.94) | |
else: | |
return R, G, B | |
if __name__ == "__main__": | |
import numpy | |
import mpmath | |
import matplotlib.pyplot as plt | |
N = 512 | |
def cplot(f, xa, xb, ya, yb, mode): | |
Z = numpy.zeros((N, N, 3)) | |
for i, x in enumerate(numpy.linspace(xa, xb, N)): | |
for j, y in enumerate(numpy.linspace(ya, yb, N)): | |
Z[j,i] = color_function(f(x+y*1j), mode=mode) | |
imgplot = plt.imshow(Z, origin='lower', interpolation='nearest') | |
plt.axis('off') | |
plt.subplot(221) | |
f = lambda z: (mpmath.fp.qp(complex(z)**4) if abs(z) < 0.99 else mpmath.inf) | |
cplot(f, -1, 1, -1, 1, mode=3) | |
plt.subplot(222) | |
f = lambda z: mpmath.fp.zeta(complex(z)) | |
cplot(f, -15, 15, -15, 15, mode=4) | |
plt.subplot(223) | |
f = lambda z: mpmath.fp.gamma(complex(z)) | |
cplot(f, -5, 5, -5, 5, mode=4) | |
plt.subplot(224) | |
f = lambda z: mpmath.fp.erf(complex(z)) | |
cplot(f, -3, 3, -3, 3, mode=6) | |
plt.tight_layout() | |
#plt.show() | |
plt.savefig("demo.png", dpi=500) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I almost vectorized it. 😆