Skip to content

[python][bug] GradingPrimary.saturation broken when used in solo. #1642

@MrLixm

Description

@MrLixm

Hello,
I have noticed that when using a GradingPrimary instance with only the saturation modified, when ported to a GradingPrimaryTransform and then applied to an image, the saturation change is not registered and the output image is unchanged.
Now if I only modified by a very little another attribute like GradingPrimary.exposure, the saturation is registered and applied to the input.

You can find a demo in the following snippet, where the test_sat_only will be the only one to fail.
(only the TestGradingPrimaryTransform.config_path need to be updated with any valid OCIO config)

# python>3
import unittest
from pathlib import Path
from typing import Tuple

import PyOpenColorIO as ocio
import numpy
import numpy.testing


def make_img(color: Tuple[float, float, float]):
    """Create a 64x64 RGB image with the given color."""
    return numpy.full((64, 64, 3), color, dtype=numpy.float32)


class TestGradingPrimaryTransform(unittest.TestCase):

    config_path = Path(
        r"YOURCONFIGPATH\config.ocio"
    )

    def setUp(self) -> None:
        self.config: ocio.Config = ocio.Config().CreateFromFile(str(self.config_path))
        self.img1 = make_img((0.5, 0.1, 0.1))
        self.gp: ocio.GradingPrimary = ocio.GradingPrimary(ocio.GRADING_LIN)
        return

    def tearDown(self) -> None:
        self.config = None
        self.img1 = None
        return

    def _apply_gp_on_img(self):

        tsfm_gp = ocio.GradingPrimaryTransform(
            self.gp,
            ocio.GRADING_LIN,
            False,
        )

        proc: ocio.Processor = self.config.getProcessor(tsfm_gp)
        proc: ocio.CPUProcessor = proc.getDefaultCPUProcessor()

        proc.applyRGB(self.img1)

        return

    def test_sat_only(self):

        # MODIFY
        p_sat = 2.0

        self.gp.saturation = p_sat

        self._apply_gp_on_img()

        expected = make_img((0.815, 0.015, 0.015))
        numpy.testing.assert_almost_equal(
            self.img1,
            expected,
            4,
            f"img1 is actually {self.img1[1][1]} while expected is {expected[1][1]}",
        )

    def test_sat_expo(self):

        # MODIFY
        p_sat = 2.0
        p_expo = 0.02  # in "stops", passthrough is 0

        self.gp.saturation = p_sat
        self.gp.exposure = ocio.GradingRGBM(0, 0, 0, p_expo)

        self._apply_gp_on_img()

        expected = make_img((0.82637, 0.01521, 0.01521))
        numpy.testing.assert_almost_equal(
            self.img1,
            expected,
            4,
            f"img1 is actually {self.img1[1][1]} while expected is {expected[1][1]}",
        )

    def test_offset_only(self):

        # MODIFY
        p_offset = 0.4

        self.gp.offset = ocio.GradingRGBM(0, 0, 0, p_offset)

        self._apply_gp_on_img()

        expected = make_img((0.9, 0.5, 0.5))
        numpy.testing.assert_almost_equal(
            self.img1,
            expected,
            4,
            f"img1 is actually {self.img1[1][1]} while expected is {expected[1][1]}",
        )

    def test_clampwhite_only(self):

        # MODIFY
        p_clampwhite = 0.4

        self.gp.clampWhite = p_clampwhite

        self._apply_gp_on_img()

        expected = make_img((0.4, 0.1, 0.1))
        numpy.testing.assert_almost_equal(
            self.img1,
            expected,
            4,
            f"img1 is actually {self.img1[1][1]} while expected is {expected[1][1]}",
        )


if __name__ == "__main__":
    unittest.main()

CONTEXT: Windows10, OCIO 2.1.0

Cheers.
Liam.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions