Created
December 2, 2021 16:23
-
-
Save MrLixm/870e13c6ae2411fd7792380518761e02 to your computer and use it in GitHub Desktop.
Demo script how to use OCIO to convert an image array from a given colorspace to CIE-XYZ values (with the given whitepoint). (inverse also works)
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
""" | |
Author: Liam Collod | |
Last Modified: 02/12/2020 | |
Demo script how to use OCIO to convert an image array from a given colorspace | |
to XYZ (with the given whitepoint). (inverse also works) | |
Requirements: | |
opencolorio==2.1.0 | |
colour-science==0.3.16 | |
""" | |
import PyOpenColorIO as ocio | |
import colour | |
import numpy | |
"""---------------------------------------------------------------------------- | |
Utilities to compute the matrix for OCIO | |
""" | |
def matrix_3x3_to_4x4(matrix): | |
""" | |
Convert a 3x3 matrix to a 4x4 matrix as such : | |
[[ value value value 0. ] | |
[ value value value 0. ] | |
[ value value value 0. ] | |
[ 0. 0. 0. 1. ]] | |
Args: | |
matrix(numpy.ndarray): | |
Returns: | |
numpy.ndarray: 4x4 matrix | |
""" | |
output = numpy.append(matrix, [[0], [0], [0]], axis=1) | |
output = numpy.append(output, [[0, 0, 0, 1]], axis=0) | |
return output | |
def matrix_format_oneline(matrix): | |
""" | |
Convert the matrix to a one line list (no nested list). | |
Args: | |
matrix(numpy.ndarray): | |
Returns: | |
list: matrix as a single depth list. | |
""" | |
output = numpy.concatenate(matrix).tolist() | |
return output | |
def matrix_format_ocio(matrix): | |
""" | |
Format the given 3x3 matrix to an OCIO parameters complient list. | |
Args: | |
matrix(numpy.ndarray): 3x3 matrix | |
Returns: | |
list: 4x4 matrix in a single line list. | |
""" | |
return matrix_format_oneline(matrix_3x3_to_4x4(matrix)) | |
def matrix_whitepoint_transform(source_whitepoint, | |
target_whitepoint, | |
transform="Bradford"): | |
""" Return the matrix to perform a chromatic adaptation with the given | |
parameters. | |
Args: | |
source_whitepoint(numpy.ndarray): source whitepoint name as xy coordinates | |
target_whitepoint(numpy.ndarray): target whitepoint name as xy coordinates | |
transform(str): method to use. | |
Returns: | |
numpy.ndarray: chromatic adaptation matrix from test viewing conditions | |
to reference viewing conditions. A 3x3 matrix. | |
""" | |
matrix = colour.adaptation.matrix_chromatic_adaptation_VonKries( | |
colour.xy_to_XYZ(source_whitepoint), | |
colour.xy_to_XYZ(target_whitepoint), | |
transform=transform | |
) | |
return matrix | |
def matrix_primaries_transform_ocio(source, | |
target, | |
source_whitepoint=None, | |
target_whitepoint=None): | |
""" By given a source and target colorspace, return the corresponding | |
colorspace conversion matrix. | |
You can use "XYZ" as a source or target. | |
Args: | |
source(str): source colorspace, use "XYZ" for CIE-XYZ. | |
target(str): target colorspace, use "XYZ" for CIE-XYZ. | |
source_whitepoint(str): whitepoint name | |
target_whitepoint(str): whitepoint name | |
Returns: | |
list of float: 4x4 matrix in a single line list. | |
""" | |
illum_1931 = colour.CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"] | |
if target == "XYZ": | |
source_cs = colour.RGB_COLOURSPACES[source] # type: colour.RGB_Colourspace | |
matrix = source_cs.matrix_RGB_to_XYZ.round(12) # type: numpy.ndarray | |
# perform chromatic adaptation between whitepoints | |
source_whitepoint = source_cs.whitepoint_name if not source_whitepoint else source_whitepoint | |
if source_whitepoint and target_whitepoint: | |
wp_m = matrix_whitepoint_transform( | |
source_whitepoint=illum_1931[source_whitepoint], | |
target_whitepoint=illum_1931[target_whitepoint], | |
transform="Bradford" | |
) | |
matrix = numpy.dot(wp_m, matrix) | |
elif source == "XYZ": | |
target_cs = colour.RGB_COLOURSPACES[target] # type: colour.RGB_Colourspace | |
matrix = target_cs.matrix_XYZ_to_RGB.round(12) # type: numpy.ndarray | |
# perform chromatic adaptation between whitepoints | |
target_whitepoint = target_cs.whitepoint_name if not target_whitepoint else target_whitepoint | |
if source_whitepoint and target_whitepoint: | |
wp_m = matrix_whitepoint_transform( | |
source_whitepoint=illum_1931[source_whitepoint], | |
target_whitepoint=illum_1931[target_whitepoint], | |
transform="Bradford" | |
) | |
matrix = numpy.dot(wp_m, matrix) | |
else: | |
source_cs = colour.RGB_COLOURSPACES[source] | |
target_cs = colour.RGB_COLOURSPACES[target] | |
matrix = colour.matrix_RGB_to_RGB(source_cs, target_cs, "Bradford") | |
return matrix_format_ocio(matrix) | |
"""---------------------------------------------------------------------------- | |
The actual script | |
""" | |
def run(): | |
""" | |
""" | |
xyz_space_name = "CIE-XYZ-D65" | |
# give a name registered by colour package | |
rgb_space_name = "DCI-P3" | |
"""============================ | |
Build a quick OCIO config first. | |
""" | |
# the config | |
config = ocio.Config() | |
# the XYZ reference space | |
cs_XYZ = ocio.ColorSpace(ocio.REFERENCE_SPACE_SCENE) | |
cs_XYZ.setName(name=xyz_space_name) | |
config.addColorSpace(cs_XYZ) | |
# the other colorspace assumed to be a RGB colorspace | |
cs_rgb_colorspace = ocio.ColorSpace(ocio.REFERENCE_SPACE_SCENE) | |
cs_rgb_colorspace.setName(rgb_space_name) | |
# compute the matrix using a function | |
# you can also just give it directly. | |
_matrix = matrix_primaries_transform_ocio( | |
source=rgb_space_name, | |
target="XYZ", | |
# you can remove this line if you don't want chromatic adaptation | |
target_whitepoint="D65" | |
) | |
_transform = ocio.MatrixTransform(_matrix) | |
cs_rgb_colorspace.setTransform(_transform, ocio.COLORSPACE_DIR_TO_REFERENCE) | |
config.addColorSpace(cs_rgb_colorspace) | |
"""=================== | |
Perform the conversion | |
""" | |
# swap the arguments to invert the transformation | |
processor = config.getProcessor( | |
rgb_space_name, | |
xyz_space_name, | |
) | |
cpu = processor.getDefaultCPUProcessor() | |
# source array | |
img = [1, 0, 0, 0] | |
img = cpu.applyRGBA(img) | |
print(img) | |
print("[run] Finished") | |
return | |
if __name__ == '__main__': | |
run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment