ah so it’s supposed to be
col = colour.log_decoding(col, function='Log2', min_exposure=-10, max_exposure=15, middle_grey=0.18)
col = transform(col, f1=f1, fi=fi)
rather than
col = colour.log_decoding(col, function='Log2', min_exposure=-10, max_exposure=15, middle_grey=0.18)
col = transform(col, f1=f1, fi=fi)
col = colour.log_encoding(col, function='Log2', min_exposure=-10, max_exposure=15, middle_grey=0.18)
?
and I see, makes sense. That’s what @troy_s meant by
The full script as it currently stands, for future reference:
import colour
import numpy as np
def subtract_mean(col):
mean = np.mean(col)
return col - mean, mean
def add_mean(col, mean):
return col + mean
def cart_to_sph(col):
r = np.linalg.norm(col)
phi = np.arctan2(col[1], col[0])
rho = np.hypot(col[0], col[1])
theta = np.arctan2(rho, col[2])
return np.array([r, phi, theta])
def sph_to_cart(col):
r = col[0]
phic = np.cos(col[1])
phis = np.sin(col[1])
thetac = np.cos(col[2])
thetas = np.sin(col[2])
x = r * phic * thetas
y = r * phis * thetas
z = r * thetac
return np.array([x, y, z])
def compress(val, f1, fi):
fiinv = 1 - fi
return val * fi/(1 - fiinv * np.power(((f1*fiinv)/(f1-fi)), -val))
def transform(col, f1, fi):
col, mean = subtract_mean(col)
col = cart_to_sph(col)
col[0] = compress(col[0], f1=f1, fi=fi)
col = sph_to_cart(col)
return add_mean(col, mean)
def main():
# resolution of the 3DLUT
LUT_res = 33
# curve shape parameters
f1 = 0.9 # how much saturation compression there is at a spherical saturation of 1
fi = 0.8 # how much saturation compression there is at a spherical saturation of infinity
LUT = colour.LUT3D(name=f'Spherical Saturation Compression {f1=} {fi=}', size=LUT_res)
min_lg2 = colour.models.log_decoding_Log2(-12.473931188, min_exposure=-10, max_exposure=15, middle_grey=0.18)
max_lg2 = colour.models.log_decoding_Log2(12.473931188, min_exposure=-10, max_exposure=15, middle_grey=0.18)
LUT.domain = ([[min_lg2, min_lg2, min_lg2], [max_lg2, max_lg2, max_lg2]])
LUT.comments = [f'Spherically compress saturation by a gentle curve. Resolution {LUT_res}',
f'Very high saturation values are reduced by {((1-fi)*100):.1f}%.',
f'At a spherical saturation of 1.0, the compression is {((1-f1)*100):.1f}%.']
x, y, z, _ = LUT.table.shape
for i in range(x):
for j in range(y):
for k in range(z):
col = np.array(LUT.table[i][j][k], dtype=np.longdouble)
col = 2 * 12.473931188 * col - 12.473931188
col = colour.models.log_decoding_Log2(col, min_exposure=-10, max_exposure=15, middle_grey=0.18)
col = transform(col, f1=f1, fi=fi)
# encoding not necessary
# col = colour.models.log_encoding_log2(col, min_exposure=-10, max_exposure=15, middle_grey=0.18)
LUT.table[i][j][k] = np.array(col, dtype=LUT.table.dtype)
colour.write_LUT(LUT, f"Spherical_Saturation_Compression_{(f1*100):.1f}_{(fi*100):.1f}_r{LUT_res}.cube")
print(LUT.table)
print(LUT)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
pass
Or wait, do I not even need
col = 2 * 12.473931188 * col - 12.473931188
? Does the log_decoding already take care of that part?
As far as I can tell that part is still needed