colourspace-explorer 1.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- colourspace_explorer-1.5/LICENSE +29 -0
- colourspace_explorer-1.5/PKG-INFO +79 -0
- colourspace_explorer-1.5/README.md +29 -0
- colourspace_explorer-1.5/pyproject.toml +27 -0
- colourspace_explorer-1.5/setup.cfg +4 -0
- colourspace_explorer-1.5/src/colourspace/__init__.py +12 -0
- colourspace_explorer-1.5/src/colourspace/convert.py +165 -0
- colourspace_explorer-1.5/src/colourspace/gamut/Cmax_res10_full.npy +0 -0
- colourspace_explorer-1.5/src/colourspace/gamut/Cmax_res10_sRGB.npy +0 -0
- colourspace_explorer-1.5/src/colourspace/gamut/Cmax_res1_full.npy +0 -0
- colourspace_explorer-1.5/src/colourspace/gamut/Cmax_res1_sRGB.npy +0 -0
- colourspace_explorer-1.5/src/colourspace/gamut/Cmax_res2_full.npy +0 -0
- colourspace_explorer-1.5/src/colourspace/gamut/Cmax_res2_sRGB.npy +0 -0
- colourspace_explorer-1.5/src/colourspace/gamut.py +367 -0
- colourspace_explorer-1.5/src/colourspace/limits/limits_360-780-10_XYZ.npy +0 -0
- colourspace_explorer-1.5/src/colourspace/limits/limits_360-780-1_XYZ.npy +0 -0
- colourspace_explorer-1.5/src/colourspace/limits/limits_360-780-20_XYZ.npy +0 -0
- colourspace_explorer-1.5/src/colourspace/limits/limits_360-780-5_XYZ.npy +0 -0
- colourspace_explorer-1.5/src/colourspace/limits.py +304 -0
- colourspace_explorer-1.5/src/colourspace/maps.py +607 -0
- colourspace_explorer-1.5/src/colourspace/slices.py +251 -0
- colourspace_explorer-1.5/src/colourspace/tutorials/1. convert.ipynb +613 -0
- colourspace_explorer-1.5/src/colourspace/tutorials/2. sRGB gamut.ipynb +738 -0
- colourspace_explorer-1.5/src/colourspace/tutorials/3. full gamut.ipynb +1294 -0
- colourspace_explorer-1.5/src/colourspace/tutorials/4. slices.ipynb +1162 -0
- colourspace_explorer-1.5/src/colourspace/tutorials/5. cmaps.ipynb +1828 -0
- colourspace_explorer-1.5/src/colourspace/tutorials/5.1 cmaps helix.ipynb +691 -0
- colourspace_explorer-1.5/src/colourspace/tutorials/copy_tutorials.py +38 -0
- colourspace_explorer-1.5/src/colourspace_explorer.egg-info/PKG-INFO +79 -0
- colourspace_explorer-1.5/src/colourspace_explorer.egg-info/SOURCES.txt +31 -0
- colourspace_explorer-1.5/src/colourspace_explorer.egg-info/dependency_links.txt +1 -0
- colourspace_explorer-1.5/src/colourspace_explorer.egg-info/requires.txt +5 -0
- colourspace_explorer-1.5/src/colourspace_explorer.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Gilles Ferrand
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: colourspace-explorer
|
|
3
|
+
Version: 1.5
|
|
4
|
+
Summary: A package providing tools for working with colours in python
|
|
5
|
+
Author-email: Gilles Ferrand <astro.ferrand@gmail.com>
|
|
6
|
+
License: BSD 3-Clause License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025, Gilles Ferrand
|
|
9
|
+
All rights reserved.
|
|
10
|
+
|
|
11
|
+
Redistribution and use in source and binary forms, with or without
|
|
12
|
+
modification, are permitted provided that the following conditions are met:
|
|
13
|
+
|
|
14
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
15
|
+
list of conditions and the following disclaimer.
|
|
16
|
+
|
|
17
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
18
|
+
this list of conditions and the following disclaimer in the documentation
|
|
19
|
+
and/or other materials provided with the distribution.
|
|
20
|
+
|
|
21
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
22
|
+
contributors may be used to endorse or promote products derived from
|
|
23
|
+
this software without specific prior written permission.
|
|
24
|
+
|
|
25
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
26
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
27
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
28
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
29
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
30
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
31
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
32
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
33
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
34
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
35
|
+
|
|
36
|
+
Project-URL: Homepage, https://github.com/gillesferrand/colourspace
|
|
37
|
+
Keywords: colour,colour space,colour maps
|
|
38
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
39
|
+
Classifier: Operating System :: OS Independent
|
|
40
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
41
|
+
Classifier: Programming Language :: Python :: 3
|
|
42
|
+
Requires-Python: >=3.7
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
License-File: LICENSE
|
|
45
|
+
Requires-Dist: numpy
|
|
46
|
+
Requires-Dist: matplotlib
|
|
47
|
+
Requires-Dist: scipy
|
|
48
|
+
Requires-Dist: colorspacious
|
|
49
|
+
Requires-Dist: colour-science
|
|
50
|
+
|
|
51
|
+
This package provides some tools to work with colour in Python in a way that matches human perception. The adopted colour space is CIELCH, the cylindrical representation of CIELAB, with coordinates L = lightness in [0,100], C = chroma >= 0, H = hue angle in [0, 360].
|
|
52
|
+
The package relies on [NumPy](https://numpy.org) for array manipulations, [Matplotlib](https://matplotlib.org) for plotting, and optionally other colour packages listed below.
|
|
53
|
+
|
|
54
|
+
**convert** provides functions to convert a colour triplet between HCL and sRGB spaces, using either built-in [formulas](http://www.easyrgb.com/en/math.php) or wrappers to [colorspacious](https://pypi.python.org/pypi/colorspacious/) or [colour](http://colour-science.org) packages.
|
|
55
|
+
|
|
56
|
+
**gamut** provides functions to compute and visualize the boundary Cmax(L,H) of the sRGB gamut and of the full human gamut. These boundaries can be obtained by two methods: (1) for each point in the LCH plane, convert the colour to the target space (RGB or XYZ) and check whether it falls within the gamut; (2) get the contour points of the gamut in the space where it is naturally defined (RGB or XYZ), and convert them back to LCH space. For the latter method, to resample the function Cmax(L,H) on a regular grid the script relies on [scipy.interpolate](https://docs.scipy.org/doc/scipy/reference/interpolate.html). Both methods assume knowledge of the gamut in some space, this is trivial for the sRGB gamut, not for the human gamut hence the limits script below.
|
|
57
|
+
The pre-computed gamuts at resolutions of 1 and 10 bins per unit L,H are included as Numpy binary files (30 MB).
|
|
58
|
+
|
|
59
|
+
**limits** provides functions to compute and visualize the limits of the human gamut (optimal colour stimuli, a.k.a. MacAdam limits). As explained on [Bruce Lindbloom's website](http://www.brucelindbloom.com/index.html?LabGamutDisplayHelp.html) they are obtained by considering all possible step-functions as spectral distributions. The conversion from light spectrum to XYZ colour is made with [colour](http://colour-science.org) (the computed limits can be compared with the crude triangulation provided by this package). To triangulate the limit surface in 3D the script relies on [scipy.spatial](https://docs.scipy.org/doc/scipy/reference/spatial.html) (this is required by method 1 above).
|
|
60
|
+
The pre-computed limits at resolutions of 1, 5, 10, 20 nm are included as Numpy binary files (4.5 MB).
|
|
61
|
+
|
|
62
|
+
**slices** provides functions to draw slices in the colour space: L-H planes as a function of C (or for the maximum possible C), C-H planes as a function of L, and L-C planes as a function of H.
|
|
63
|
+
|
|
64
|
+
**maps** provides functions to generate colour maps for Matplotlib, and/or written as png images. Three kinds of cmaps are implemented, which separate the different perceptual dimensions: equi-luminant (varying only in H, at the max C), diverging (varying only in C between two hues H1 and H2), and mono-hue (varying only in L, at the maximum C). It includes utility functions for testing a cmap on mock data, and for displaying the path taken by a cmap in the colour space.
|
|
65
|
+
|
|
66
|
+
The package comes with five notebooks, which demonstrate the use of the five scripts.
|
|
67
|
+
The notebook files can be copied in the current directory using function colourspace.copy_tutorials() found in script tutorials/copy_tutorials.py.
|
|
68
|
+
Static renderings of the notebooks can also be seen [on nbviewer](https://nbviewer.org/github/gillesferrand/colourspace/tree/master/src/colourspace/tutorials/):
|
|
69
|
+
[1. Conversions between colour spaces](https://nbviewer.org/github/gillesferrand/colourspace/blob/master/src/colourspace/tutorials/1.%20convert.ipynb)
|
|
70
|
+
[2. Mapping the sRGB gamut](https://nbviewer.org/github/gillesferrand/colourspace/blob/master/src/colourspace/tutorials/2.%20sRGB%20gamut.ipynb)
|
|
71
|
+
[3. Mapping the human gamut](https://nbviewer.org/github/gillesferrand/colourspace/blob/master/src/colourspace/tutorials/3.%20full%20gamut.ipynb)
|
|
72
|
+
[4. Slicing the LCH space](https://nbviewer.org/github/gillesferrand/colourspace/blob/master/src/colourspace/tutorials/4.%20slices.ipynb)
|
|
73
|
+
[5. Making custom colour maps](https://nbviewer.org/github/gillesferrand/colourspace/blob/master/src/colourspace/tutorials/5.%20cmaps.ipynb)
|
|
74
|
+
Additional case study:
|
|
75
|
+
[5.1 Helix colour scheme](https://nbviewer.org/github/gillesferrand/colourspace/blob/master/src/colourspace/tutorials/5.1%20cmaps%20helix.ipynb)
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
This package is provided "as is", with no warranty of any kind.
|
|
79
|
+
The code can be freely used by whoever wants to explore modern colour theory.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
This package provides some tools to work with colour in Python in a way that matches human perception. The adopted colour space is CIELCH, the cylindrical representation of CIELAB, with coordinates L = lightness in [0,100], C = chroma >= 0, H = hue angle in [0, 360].
|
|
2
|
+
The package relies on [NumPy](https://numpy.org) for array manipulations, [Matplotlib](https://matplotlib.org) for plotting, and optionally other colour packages listed below.
|
|
3
|
+
|
|
4
|
+
**convert** provides functions to convert a colour triplet between HCL and sRGB spaces, using either built-in [formulas](http://www.easyrgb.com/en/math.php) or wrappers to [colorspacious](https://pypi.python.org/pypi/colorspacious/) or [colour](http://colour-science.org) packages.
|
|
5
|
+
|
|
6
|
+
**gamut** provides functions to compute and visualize the boundary Cmax(L,H) of the sRGB gamut and of the full human gamut. These boundaries can be obtained by two methods: (1) for each point in the LCH plane, convert the colour to the target space (RGB or XYZ) and check whether it falls within the gamut; (2) get the contour points of the gamut in the space where it is naturally defined (RGB or XYZ), and convert them back to LCH space. For the latter method, to resample the function Cmax(L,H) on a regular grid the script relies on [scipy.interpolate](https://docs.scipy.org/doc/scipy/reference/interpolate.html). Both methods assume knowledge of the gamut in some space, this is trivial for the sRGB gamut, not for the human gamut hence the limits script below.
|
|
7
|
+
The pre-computed gamuts at resolutions of 1 and 10 bins per unit L,H are included as Numpy binary files (30 MB).
|
|
8
|
+
|
|
9
|
+
**limits** provides functions to compute and visualize the limits of the human gamut (optimal colour stimuli, a.k.a. MacAdam limits). As explained on [Bruce Lindbloom's website](http://www.brucelindbloom.com/index.html?LabGamutDisplayHelp.html) they are obtained by considering all possible step-functions as spectral distributions. The conversion from light spectrum to XYZ colour is made with [colour](http://colour-science.org) (the computed limits can be compared with the crude triangulation provided by this package). To triangulate the limit surface in 3D the script relies on [scipy.spatial](https://docs.scipy.org/doc/scipy/reference/spatial.html) (this is required by method 1 above).
|
|
10
|
+
The pre-computed limits at resolutions of 1, 5, 10, 20 nm are included as Numpy binary files (4.5 MB).
|
|
11
|
+
|
|
12
|
+
**slices** provides functions to draw slices in the colour space: L-H planes as a function of C (or for the maximum possible C), C-H planes as a function of L, and L-C planes as a function of H.
|
|
13
|
+
|
|
14
|
+
**maps** provides functions to generate colour maps for Matplotlib, and/or written as png images. Three kinds of cmaps are implemented, which separate the different perceptual dimensions: equi-luminant (varying only in H, at the max C), diverging (varying only in C between two hues H1 and H2), and mono-hue (varying only in L, at the maximum C). It includes utility functions for testing a cmap on mock data, and for displaying the path taken by a cmap in the colour space.
|
|
15
|
+
|
|
16
|
+
The package comes with five notebooks, which demonstrate the use of the five scripts.
|
|
17
|
+
The notebook files can be copied in the current directory using function colourspace.copy_tutorials() found in script tutorials/copy_tutorials.py.
|
|
18
|
+
Static renderings of the notebooks can also be seen [on nbviewer](https://nbviewer.org/github/gillesferrand/colourspace/tree/master/src/colourspace/tutorials/):
|
|
19
|
+
[1. Conversions between colour spaces](https://nbviewer.org/github/gillesferrand/colourspace/blob/master/src/colourspace/tutorials/1.%20convert.ipynb)
|
|
20
|
+
[2. Mapping the sRGB gamut](https://nbviewer.org/github/gillesferrand/colourspace/blob/master/src/colourspace/tutorials/2.%20sRGB%20gamut.ipynb)
|
|
21
|
+
[3. Mapping the human gamut](https://nbviewer.org/github/gillesferrand/colourspace/blob/master/src/colourspace/tutorials/3.%20full%20gamut.ipynb)
|
|
22
|
+
[4. Slicing the LCH space](https://nbviewer.org/github/gillesferrand/colourspace/blob/master/src/colourspace/tutorials/4.%20slices.ipynb)
|
|
23
|
+
[5. Making custom colour maps](https://nbviewer.org/github/gillesferrand/colourspace/blob/master/src/colourspace/tutorials/5.%20cmaps.ipynb)
|
|
24
|
+
Additional case study:
|
|
25
|
+
[5.1 Helix colour scheme](https://nbviewer.org/github/gillesferrand/colourspace/blob/master/src/colourspace/tutorials/5.1%20cmaps%20helix.ipynb)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
This package is provided "as is", with no warranty of any kind.
|
|
29
|
+
The code can be freely used by whoever wants to explore modern colour theory.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
[tool.setuptools]
|
|
5
|
+
package-dir = {"" = "src"}
|
|
6
|
+
[tool.setuptools.package-data]
|
|
7
|
+
colourspace = ["gamut/*.npy","limits/*.npy","tutorials/*.ipynb"]
|
|
8
|
+
|
|
9
|
+
[project]
|
|
10
|
+
name = "colourspace-explorer"
|
|
11
|
+
version = "1.5"
|
|
12
|
+
description = "A package providing tools for working with colours in python"
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
authors = [{ name = "Gilles Ferrand", email = "astro.ferrand@gmail.com" }]
|
|
15
|
+
license = { file = "LICENSE" }
|
|
16
|
+
requires-python = ">=3.7"
|
|
17
|
+
dependencies = ["numpy", "matplotlib", "scipy", "colorspacious", "colour-science"]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 5 - Production/Stable",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"License :: OSI Approved :: BSD License",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
]
|
|
24
|
+
keywords = ["colour","colour space","colour maps",]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
"Homepage" = "https://github.com/gillesferrand/colourspace"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
__author__ = "Gilles Ferrand (U. of Manitoba / RIKEN)"
|
|
4
|
+
__version__ = "1.5"
|
|
5
|
+
|
|
6
|
+
from . import convert
|
|
7
|
+
from . import gamut
|
|
8
|
+
from . import limits
|
|
9
|
+
from . import slices
|
|
10
|
+
from . import maps
|
|
11
|
+
|
|
12
|
+
from .tutorials.copy_tutorials import copy_tutorials
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Conversion between colour spaces: CIE LCH <-> CIE Lab <-> CIE XYZ <->sRGB
|
|
4
|
+
Can use either:
|
|
5
|
+
- custom formulas taken from http://www.easyrgb.com/en/math.php
|
|
6
|
+
- wrapper to "colorspacious" package from https://pypi.python.org/pypi/colorspacious/
|
|
7
|
+
- wrapper to "colour" package from http://colour-science.org
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import print_function
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
convertor = None
|
|
14
|
+
illuminant = None
|
|
15
|
+
LCH2RGB = lambda L,C,H: [np.nan, np.nan, np.nan]
|
|
16
|
+
RGB2LCH = lambda R,G,B: [np.nan, np.nan, np.nan]
|
|
17
|
+
|
|
18
|
+
def set_convertor(name, ill='D65'):
|
|
19
|
+
""" Binds the conversion functions LCH2RGB() and RGB2LCH() to the choosen colour package
|
|
20
|
+
"""
|
|
21
|
+
global LCH2RGB, RGB2LCH, convertor, illuminant
|
|
22
|
+
if name not in ['custom', 'colorspacious', 'colourscience']:
|
|
23
|
+
print("Unknown conversion module")
|
|
24
|
+
return
|
|
25
|
+
convertor = name
|
|
26
|
+
illuminant = ill
|
|
27
|
+
if name=='custom':
|
|
28
|
+
LCH2RGB = lambda L,C,H: XYZ2RGB(Lab2XYZ(LCH2Lab((L,C,H))))
|
|
29
|
+
RGB2LCH = lambda R,G,B: Lab2LCH(XYZ2Lab(RGB2XYZ((R,G,B))))
|
|
30
|
+
if name=='colorspacious':
|
|
31
|
+
from colorspacious import cspace_convert
|
|
32
|
+
func_LCH2RGB = lambda L,C,H: cspace_convert([L,C,H], {"name": "CIELCh", "XYZ100_w": ill}, "sRGB1")
|
|
33
|
+
func_RGB2LCH = lambda R,G,B: cspace_convert([R,G,B], "sRGB1", {"name": "CIELCh", "XYZ100_w": ill})
|
|
34
|
+
if name=='colourscience':
|
|
35
|
+
import colour
|
|
36
|
+
if hasattr(colour,'ILLUMINANTS' ): cs_ill = colour.ILLUMINANTS ['CIE 1931 2 Degree Standard Observer'][ill]
|
|
37
|
+
if hasattr(colour,'CCS_ILLUMINANTS'): cs_ill = colour.CCS_ILLUMINANTS['CIE 1931 2 Degree Standard Observer'][ill]
|
|
38
|
+
func_LCH2RGB = lambda L,C,H: colour.XYZ_to_sRGB(colour.Lab_to_XYZ(colour.LCHab_to_Lab([L,C,H]), illuminant=cs_ill))
|
|
39
|
+
func_RGB2LCH = lambda R,G,B: colour.Lab_to_LCHab(colour.XYZ_to_Lab(colour.sRGB_to_XYZ([R,G,B]), illuminant=cs_ill))
|
|
40
|
+
if name=='colorspacious' or name=='colourscience':
|
|
41
|
+
def LCH2RGB(L,C,H):
|
|
42
|
+
if hasattr(L, '__iter__'):
|
|
43
|
+
RGB = np.array(list(map(func_LCH2RGB,L,C,H)))
|
|
44
|
+
R = RGB[:,0]
|
|
45
|
+
G = RGB[:,1]
|
|
46
|
+
B = RGB[:,2]
|
|
47
|
+
else:
|
|
48
|
+
R,G,B = func_LCH2RGB(L,C,H)
|
|
49
|
+
return R, G, B
|
|
50
|
+
def RGB2LCH(R,G,B):
|
|
51
|
+
if hasattr(R, '__iter__'):
|
|
52
|
+
LCH = np.array(list(map(func_RGB2LCH,R,G,B)))
|
|
53
|
+
L = LCH[:,0]
|
|
54
|
+
C = LCH[:,1]
|
|
55
|
+
H = LCH[:,2]
|
|
56
|
+
else:
|
|
57
|
+
L,C,H = func_RGB2LCH(R,G,B)
|
|
58
|
+
return L, C, H
|
|
59
|
+
print("convertor = '%s' (illuminant = '%s')"%(name,illuminant))
|
|
60
|
+
|
|
61
|
+
set_convertor('custom')
|
|
62
|
+
|
|
63
|
+
# cartesian CIE Lab <-> cylindrical CIE LCH
|
|
64
|
+
|
|
65
|
+
def LCH2Lab(LCH):
|
|
66
|
+
L,C,H = LCH
|
|
67
|
+
#L = np.float(L)
|
|
68
|
+
a = C * np.cos(H*2*np.pi/360.)
|
|
69
|
+
b = C * np.sin(H*2*np.pi/360.)
|
|
70
|
+
return (L, a, b)
|
|
71
|
+
|
|
72
|
+
def Lab2LCH(Lab):
|
|
73
|
+
L,a,b = Lab
|
|
74
|
+
#L = np.float(L)
|
|
75
|
+
C = np.sqrt(a**2+b**2)
|
|
76
|
+
H = np.arctan2(b,a) * 360/(2*np.pi)
|
|
77
|
+
H = np.where(H<0, H+360, H)
|
|
78
|
+
return (L, C, H+0.)
|
|
79
|
+
|
|
80
|
+
# perceptual CIE XYZ <-> uniform CIE Lab
|
|
81
|
+
|
|
82
|
+
# standard illuminants for 2° observer
|
|
83
|
+
Xn = {}
|
|
84
|
+
Yn = {}
|
|
85
|
+
Zn = {}
|
|
86
|
+
# D65
|
|
87
|
+
Xn['D65'] = 95.047
|
|
88
|
+
Yn['D65'] = 100.000
|
|
89
|
+
Zn['D65'] = 108.883
|
|
90
|
+
# D50
|
|
91
|
+
Xn['D50'] = 96.422
|
|
92
|
+
Yn['D50'] = 100.000
|
|
93
|
+
Zn['D50'] = 82.521
|
|
94
|
+
|
|
95
|
+
def XYZ2Lab(XYZ):
|
|
96
|
+
X,Y,Z = XYZ
|
|
97
|
+
L = 116 * f_forward(Y/Yn[illuminant]) - 16
|
|
98
|
+
a = 500 * (f_forward(X/Xn[illuminant]) - f_forward(Y/Yn[illuminant]))
|
|
99
|
+
b = 200 * (f_forward(Y/Yn[illuminant]) - f_forward(Z/Zn[illuminant]))
|
|
100
|
+
return (L,a,b)
|
|
101
|
+
|
|
102
|
+
def Lab2XYZ(Lab):
|
|
103
|
+
L,a,b = Lab
|
|
104
|
+
X = Xn[illuminant] * f_reverse((L+16)/116.+a/500.)
|
|
105
|
+
Y = Yn[illuminant] * f_reverse((L+16)/116.)
|
|
106
|
+
Z = Zn[illuminant] * f_reverse((L+16)/116.-b/200.)
|
|
107
|
+
return (X,Y,Z)
|
|
108
|
+
|
|
109
|
+
def f_forward(x):
|
|
110
|
+
return np.where(x > (6/29.)**3, x**(1/3.), 1/3.*(29/6.)**2*x+4/29.)
|
|
111
|
+
|
|
112
|
+
def f_reverse(x):
|
|
113
|
+
return np.where(x > 6/29., x**3, 3*(6/29.)**2*(x-4/29.))
|
|
114
|
+
|
|
115
|
+
# human CIE XYZ <-> machine sRGB
|
|
116
|
+
|
|
117
|
+
RGB_max = 1.
|
|
118
|
+
|
|
119
|
+
def XYZ2RGB(XYZ):
|
|
120
|
+
X,Y,Z = XYZ
|
|
121
|
+
R = RGB_max * gamma_forward( +3.2406 * X/100. -1.5372 * Y/100. -0.4986 * Z/100. )
|
|
122
|
+
G = RGB_max * gamma_forward( -0.9689 * X/100. +1.8758 * Y/100. +0.0415 * Z/100. )
|
|
123
|
+
B = RGB_max * gamma_forward( +0.0557 * X/100. -0.2040 * Y/100. +1.0570 * Z/100. )
|
|
124
|
+
return (R,G,B)
|
|
125
|
+
|
|
126
|
+
def RGB2XYZ(RGB):
|
|
127
|
+
R,G,B = RGB
|
|
128
|
+
X = 100 * ( +0.4124 * gamma_reverse(R/RGB_max) +0.3576 * gamma_reverse(G/RGB_max) +0.1805 * gamma_reverse(B/RGB_max) )
|
|
129
|
+
Y = 100 * ( +0.2126 * gamma_reverse(R/RGB_max) +0.7152 * gamma_reverse(G/RGB_max) +0.0722 * gamma_reverse(B/RGB_max) )
|
|
130
|
+
Z = 100 * ( +0.0193 * gamma_reverse(R/RGB_max) +0.1192 * gamma_reverse(G/RGB_max) +0.9505 * gamma_reverse(B/RGB_max) )
|
|
131
|
+
return (X,Y,Z)
|
|
132
|
+
|
|
133
|
+
def gamma_forward(Cln):
|
|
134
|
+
mask = Cln <= 0.0031308
|
|
135
|
+
g = np.empty_like(Cln)
|
|
136
|
+
g[ mask] = 12.92*Cln[mask]
|
|
137
|
+
g[~mask] = 1.055*(Cln[~mask]**(1/2.4))-0.055
|
|
138
|
+
return g
|
|
139
|
+
|
|
140
|
+
def gamma_reverse(Cnl):
|
|
141
|
+
mask = Cnl <= 0.04045
|
|
142
|
+
g = np.empty_like(Cnl)
|
|
143
|
+
g[ mask] = Cnl[mask]/12.92
|
|
144
|
+
g[~mask] = ((Cnl[~mask]+0.055)/1.055)**2.4
|
|
145
|
+
return g
|
|
146
|
+
|
|
147
|
+
# RGB gamut check
|
|
148
|
+
|
|
149
|
+
def crop1(R,min=0,max=1):
|
|
150
|
+
R_crop = np.where(R < min, np.nan, R )
|
|
151
|
+
R_crop = np.where(R_crop > max, np.nan, R_crop)
|
|
152
|
+
return R_crop
|
|
153
|
+
|
|
154
|
+
def crop3(RGB,min=0,max=1):
|
|
155
|
+
R,G,B = RGB
|
|
156
|
+
return np.stack((crop1(R,min,max),crop1(G,min,max),crop1(B,min,max)),axis=-1)
|
|
157
|
+
|
|
158
|
+
def clip1(R,min=0,max=1):
|
|
159
|
+
R_clip = np.where(R < min, min, R )
|
|
160
|
+
R_clip = np.where(R_clip > max, max, R_clip)
|
|
161
|
+
return R_clip
|
|
162
|
+
|
|
163
|
+
def clip3(RGB,min=0,max=1):
|
|
164
|
+
R,G,B = RGB
|
|
165
|
+
return np.stack((clip1(R,min,max),clip1(G,min,max),clip1(B,min,max)),axis=-1)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|