-
Notifications
You must be signed in to change notification settings - Fork 14
/
gamut_compress.py
85 lines (61 loc) · 2.77 KB
/
gamut_compress.py
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
# -*- coding: utf-8 -*-
"""
Gamut Compress
==============
"""
from __future__ import division, unicode_literals
import numpy as np
__all__ = ['compression_function', 'gamut_compression_operator']
# *****************************************************************************
# Preserving as much as possible of the "GamutCompress.glsl" file formatting.
# The bisection code could be removed entirely and replaced with "np.solve".
# This is not Pythonic nor great but will make update subsequent easier.
# *****************************************************************************
# yapf: disable
# calculate compressed distance
def compress(dist, lim, thr, invert, power):
# power(p) compression function plot https://www.desmos.com/calculator/54aytu7hek
s = (lim-thr)/np.power(np.power((1-thr)/(lim-thr),-power)-1,1/power) # calc y=1 intersect
if not invert:
cdist = thr+s*((dist-thr)/s)/(np.power(1+np.power((dist-thr)/s,power),1/power)) # compress
else:
cdist = thr+s*np.power(-(np.power((dist-thr)/s,power)/(np.power((dist-thr)/s,power)-1)),1/power) # uncompress
cdist = np.nan_to_num(cdist)
cdist[dist < thr] = dist[dist < thr]
return cdist
def main(rgb, invert=False, threshold=[0.815, 0.803, 0.88], cyan=0.147, magenta=0.264, yellow=0.312, power=1.2):
rgb = np.asarray(rgb)
threshold = np.asarray(threshold)
if not threshold.shape:
threshold = np.tile(threshold, 3)
# thr is the percentage of the core gamut to protect.
thr = np.clip(threshold, -np.inf, 0.9999).reshape(
[1] * (rgb.ndim - 1) + [3])
# lim is the max distance from the gamut boundary that will be compressed
# 0 is a no-op, 1 will compress colors from a distance of 2 from achromatic to the gamut boundary
lim = np.array([cyan+1, magenta+1, yellow+1])
# achromatic axis
ach = np.max(rgb, axis=-1)[..., np.newaxis]
# distance from the achromatic axis for each color component aka inverse rgb ratios
dist = np.where(ach == 0.0, 0.0, (ach-rgb)/np.abs(ach))
# compress distance with user controlled parameterized shaper function
cdist = compress(dist, lim, thr, invert, power)
crgb = ach-cdist*np.abs(ach)
return crgb
# yapf: enable
compression_function = compress
gamut_compression_operator = main
def generate_test_images(samples=16):
try:
import colour
except:
print(
'"colour-science" must be installed to generate the test images!')
return
np.random.seed(4)
RGB = (np.random.random([samples, samples, 3]) - 0.5) * 4
name_template = 'Gamut_Compress_{0}.exr'
colour.write_image(RGB, name_template.format('Reference'))
colour.write_image(gamut_compression_operator(RGB), name_template.format('PowerP'))
if __name__ == '__main__':
generate_test_images()