Skip to content

Instantly share code, notes, and snippets.

@MrLixm
Created December 2, 2021 16:23
Show Gist options
  • Save MrLixm/870e13c6ae2411fd7792380518761e02 to your computer and use it in GitHub Desktop.
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)
"""
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