Skip to content

Incoherent behaviour of "colour.SpectralDistribution.interpolate" and "colour.MultiSpectralDistributions.interpolate" methods. #566

@KelSolaar

Description

@KelSolaar

I noticed this a while ago and had that in my bottom-less stack of stuff to address:

colour.SpectralDistribution.interpolate and colour.MultiSpectralDistributions.interpolate methods should probably use underlying interpolator.

Context

The logic used by the colour.SpectralDistribution.interpolate and colour.MultiSpectralDistributions.interpolate methods to choose the interpolator was implemented as follows:

if interpolator is None:
    if self.is_uniform():
        interpolator = SpragueInterpolator
    else:
        interpolator = CubicSplineInterpolator

Basically, if the interpolator argument is not passed, the spectral distribution uniformity is tested and the Sprague (1880) method is used for interpolating if the wavelengths are uniform and the Cubic Spline method is used if they are not, according to CIE 167:2005 recommendation.

However, this is not applicable to most illuminants and light sources because their spectrum often possesses emission spikes:

image

image

Coincidentally, the CIE recommends using linear interpolation for the CIE Illuminant D Series, thus we use colour.LinearInterpolator as the default instantiation time interpolation class for all our illuminants and light sources. Note that we might in the future test for smoothness to choose the appropriate method.

Problem

The problem lies in the fact that when the colour.SpectralDistribution.align, colour.MultiSpectralDistributions.align, colour.SpectralDistribution.interpolate and colour.MultiSpectralDistributions.interpolate methods are invoked, they do not honour the instantiation time interpolation class and will forcibly use either the colour.SpragueInterpolator or colour.CubicSplineInterpolator classes unless an interpolator argument is passed.

This behaviour is not desirable and we are changing it so that if the instantiation time interpolation class is not one of colour.SpragueInterpolator or colour.CubicSplineInterpolator, then, it will be used instead and thus will take precedence over the CIE 167:2005 recommendation.

The new logic is as follows:

if interpolator is None:
    if self.interpolator not in (SpragueInterpolator,
                                 CubicSplineInterpolator):
        interpolator = self.interpolator
    elif self.is_uniform():
        interpolator = SpragueInterpolator
    else:
        interpolator = CubicSplineInterpolator

The immediate consequence is small numerical changes to various spectral computations, for example:

     >>> illuminant = ILLUMINANTS_SDS['D65']
     >>> sd_to_XYZ_tristimulus_weighting_factors_ASTME308(
     ...     sd, cmfs, illuminant)  # doctest: +ELLIPSIS
-    array([ 10.8402899...,   9.6843539...,   6.2160858...])
+    array([ 10.8405832...,   9.6844909...,   6.2155622...])
     >>> from colour import COLOURCHECKERS_SDS
     >>> sd = COLOURCHECKERS_SDS['ColorChecker N Ohta']['dark skin']
     >>> sd_to_aces_relative_exposure_values(sd)  # doctest: +ELLIPSIS
-    array([ 0.1171785...,  0.0866347...,  0.0589707...])
+    array([ 0.1171814...,  0.0866360...,  0.0589726...])

Colour quality metrics are affected a bit more severely:

```diff
     >>> from colour import ILLUMINANTS_SDS
     >>> sd = ILLUMINANTS_SDS['FL2']
     >>> colour_rendering_index(sd)  # doctest: +ELLIPSIS
-    64.1515202...
+    64.2337241...
     >>> from colour import ILLUMINANTS_SDS
     >>> sd = ILLUMINANTS_SDS['FL2']
     >>> colour_quality_scale(sd)  # doctest: +ELLIPSIS
-    64.0172835...
+    64.1117031...

We will also need to update some chromaticity coordinates of various illuminants accordingly.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions