Пример #1
0
    def test_extend_line_segment(self):
        """
        Tests :func:`colour.algebra.geometry.extend_line_segment` definition.
        """

        np.testing.assert_almost_equal(
            extend_line_segment(
                np.array([0.95694934, 0.13720932]),
                np.array([0.28382835, 0.60608318])),
            np.array([-0.5367248, 1.17765341]),
            decimal=7)

        np.testing.assert_almost_equal(
            extend_line_segment(
                np.array([0.95694934, 0.13720932]),
                np.array([0.28382835, 0.60608318]), 5),
            np.array([-3.81893739, 3.46393435]),
            decimal=7)

        np.testing.assert_almost_equal(
            extend_line_segment(
                np.array([0.95694934, 0.13720932]),
                np.array([0.28382835, 0.60608318]), -1),
            np.array([1.1043815, 0.03451295]),
            decimal=7)
Пример #2
0
    def test_extend_line_segment(self):
        """
        Tests :func:`colour.algebra.geometry.extend_line_segment` definition.
        """

        np.testing.assert_almost_equal(
            extend_line_segment(
                np.array([0.95694934, 0.13720932]),
                np.array([0.28382835, 0.60608318])),
            np.array([-0.5367248, 1.17765341]),
            decimal=7)

        np.testing.assert_almost_equal(
            extend_line_segment(
                np.array([0.95694934, 0.13720932]),
                np.array([0.28382835, 0.60608318]),
                5),
            np.array([-3.81893739, 3.46393435]),
            decimal=7)

        np.testing.assert_almost_equal(
            extend_line_segment(
                np.array([0.95694934, 0.13720932]),
                np.array([0.28382835, 0.60608318]),
                -1),
            np.array([1.1043815, 0.03451295]),
            decimal=7)
Пример #3
0
def closest_spectral_locus_wavelength(xy, xy_n, xy_s, inverse=False):
    """
    Returns the coordinates and closest spectral locus wavelength index to the
    point where the line defined by the given achromatic stimulus :math:`xy_n`
    to colour stimulus :math:`xy_n` *CIE xy* chromaticity coordinates
    intersects the spectral locus.

    Parameters
    ----------
    xy : array_like
        Colour stimulus *CIE xy* chromaticity coordinates.
    xy_n : array_like
        Achromatic stimulus *CIE xy* chromaticity coordinates.
    xy_s : array_like
        Spectral locus *CIE xy* chromaticity coordinates.
    inverse : bool, optional
        The intersection will be computed using the colour stimulus :math:`xy`
        to achromatic stimulus :math:`xy_n` inverse direction.

    Returns
    -------
    tuple
        Closest wavelength index, intersection point *CIE xy* chromaticity
        coordinates.

    Raises
    ------
    ValueError
        If no closest spectral locus wavelength index and coordinates found.

    Examples
    --------
    >>> xy = np.array([0.54369557, 0.32107944])
    >>> xy_n = np.array([0.31270000, 0.32900000])
    >>> xy_s = XYZ_to_xy(CMFS['CIE 1931 2 Degree Standard Observer'].values)
    >>> ix, intersect = closest_spectral_locus_wavelength(xy, xy_n, xy_s)
    >>> print(ix) #
    256
    >>> print(intersect) # doctest: +ELLIPSIS
    [ 0.6835474...  0.3162840...]
    """

    xy = as_float_array(xy)
    xy_n = np.resize(xy_n, xy.shape)
    xy_s = as_float_array(xy_s)

    xy_e = (extend_line_segment(xy, xy_n) if inverse else extend_line_segment(
        xy_n, xy))

    # Closing horse-shoe shape to handle line of purples intersections.
    xy_s = np.vstack([xy_s, xy_s[0, :]])

    xy_wl = intersect_line_segments(
        np.concatenate((xy_n, xy_e), -1),
        np.hstack([xy_s, np.roll(xy_s, 1, axis=0)])).xy
    xy_wl = xy_wl[~np.isnan(xy_wl).any(axis=-1)]
    if not len(xy_wl):
        raise ValueError(
            'No closest spectral locus wavelength index and coordinates found '
            'for "{0}" colour stimulus and "{1}" achromatic stimulus "xy" '
            'chromaticity coordinates!'.format(xy, xy_n))

    i_wl = np.argmin(scipy.spatial.distance.cdist(xy_wl, xy_s), axis=-1)

    i_wl = np.reshape(i_wl, xy.shape[0:-1])
    xy_wl = np.reshape(xy_wl, xy.shape)

    return i_wl, xy_wl
Пример #4
0
def dominant_wavelength(xy,
                        xy_n,
                        cmfs=CMFS['CIE 1931 2 Degree Standard Observer'],
                        inverse=False):
    """
    Returns the *dominant wavelength* :math:`\\lambda_d` for given colour
    stimulus :math:`xy` and the related :math:`xy_wl` first and :math:`xy_{cw}`
    second intersection coordinates with the spectral locus.

    In the eventuality where the :math:`xy_wl` first intersection coordinates
    are on the line of purples, the *complementary wavelength* will be
    computed in lieu.

    The *complementary wavelength* is indicated by a negative sign
    and the :math:`xy_{cw}` second intersection coordinates which are set by
    default to the same value than :math:`xy_wl` first intersection coordinates
    will be set to the *complementary dominant wavelength* intersection
    coordinates with the spectral locus.

    Parameters
    ----------
    xy : array_like
        Colour stimulus *CIE xy* chromaticity coordinates.
    xy_n : array_like
        Achromatic stimulus *CIE xy* chromaticity coordinates.
    cmfs : XYZ_ColourMatchingFunctions, optional
        Standard observer colour matching functions.
    inverse : bool, optional
        Inverse the computation direction to retrieve the
        *complementary wavelength*.

    Returns
    -------
    tuple
        *Dominant wavelength*, first intersection point *CIE xy* chromaticity
        coordinates, second intersection point *CIE xy* chromaticity
        coordinates.

    References
    ----------
    :cite:`CIETC1-482004o`, :cite:`Erdogana`

    Examples
    --------
    *Dominant wavelength* computation:

    >>> from pprint import pprint
    >>> xy = np.array([0.54369557, 0.32107944])
    >>> xy_n = np.array([0.31270000, 0.32900000])
    >>> cmfs = CMFS['CIE 1931 2 Degree Standard Observer']
    >>> pprint(dominant_wavelength(xy, xy_n, cmfs))  # doctest: +ELLIPSIS
    (array(616...),
     array([ 0.6835474...,  0.3162840...]),
     array([ 0.6835474...,  0.3162840...]))

    *Complementary dominant wavelength* is returned if the first intersection
    is located on the line of purples:

    >>> xy = np.array([0.37605506, 0.24452225])
    >>> pprint(dominant_wavelength(xy, xy_n, cmfs))  # doctest: +ELLIPSIS
    (array(-509.0),
     array([ 0.4572314...,  0.1362814...]),
     array([ 0.0104096...,  0.7320745...]))
    """

    xy = as_float_array(xy)
    xy_n = np.resize(xy_n, xy.shape)

    xy_s = XYZ_to_xy(cmfs.values)

    i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, xy_s, inverse)
    xy_cwl = xy_wl
    wl = cmfs.wavelengths[i_wl]

    xy_e = (extend_line_segment(xy, xy_n) if inverse else extend_line_segment(
        xy_n, xy))
    intersect = intersect_line_segments(np.concatenate((xy_n, xy_e), -1),
                                        np.hstack([xy_s[0],
                                                   xy_s[-1]])).intersect
    intersect = np.reshape(intersect, wl.shape)

    i_wl_r, xy_cwl_r = closest_spectral_locus_wavelength(
        xy, xy_n, xy_s, not inverse)
    wl_r = -cmfs.wavelengths[i_wl_r]

    wl = np.where(intersect, wl_r, wl)
    xy_cwl = np.where(intersect[..., np.newaxis], xy_cwl_r, xy_cwl)

    return wl, np.squeeze(xy_wl), np.squeeze(xy_cwl)
Пример #5
0
def closest_spectral_locus_wavelength(xy, xy_n, xy_s, reverse=False):
    """
    Returns the coordinates and closest spectral locus wavelength index to the
    point where the line defined by the given achromatic stimulus :math:`xy_n`
    to colour stimulus :math:`xy_n` *xy* chromaticity coordinates intersects
    the spectral locus.

    Parameters
    ----------
    xy : array_like
        Colour stimulus *xy* chromaticity coordinates.
    xy_n : array_like
        Achromatic stimulus *xy* chromaticity coordinates.
    xy_s : array_like
        Spectral locus *xy* chromaticity coordinates.
    reverse : bool, optional
        The intersection will be computed using the colour stimulus :math:`xy`
        to achromatic stimulus :math:`xy_n` reverse direction.

    Returns
    -------
    tuple
        Closest wavelength index, intersection point *xy* chromaticity
        coordinates.

    Raises
    ------
    ValueError
        If no closest spectral locus wavelength index and coordinates found.

    Examples
    --------
    >>> xy = np.array([0.54369557, 0.32107944])
    >>> xy_n = np.array([0.31270000, 0.32900000])
    >>> xy_s = XYZ_to_xy(CMFS['CIE 1931 2 Degree Standard Observer'].values)
    >>> ix, intersect = closest_spectral_locus_wavelength(xy, xy_n, xy_s)
    >>> print(ix) #
    256
    >>> print(intersect) # doctest: +ELLIPSIS
    [ 0.6835474...  0.3162840...]
    """

    xy = as_float_array(xy)
    xy_n = np.resize(xy_n, xy.shape)
    xy_s = as_float_array(xy_s)

    xy_e = (extend_line_segment(xy, xy_n)
            if reverse else extend_line_segment(xy_n, xy))

    # Closing horse-shoe shape to handle line of purples intersections.
    xy_s = np.vstack([xy_s, xy_s[0, :]])

    xy_wl = intersect_line_segments(
        np.concatenate((xy_n, xy_e), -1),
        np.hstack([xy_s, np.roll(xy_s, 1, axis=0)])).xy
    xy_wl = xy_wl[~np.isnan(xy_wl).any(axis=-1)]
    if not len(xy_wl):
        raise ValueError(
            'No closest spectral locus wavelength index and coordinates found '
            'for "{0}" colour stimulus and "{1}" achromatic stimulus "xy" '
            'chromaticity coordinates!'.format(xy, xy_n))

    i_wl = np.argmin(scipy.spatial.distance.cdist(xy_wl, xy_s), axis=-1)

    i_wl = np.reshape(i_wl, xy.shape[0:-1])
    xy_wl = np.reshape(xy_wl, xy.shape)

    return i_wl, xy_wl
Пример #6
0
def dominant_wavelength(xy,
                        xy_n,
                        cmfs=CMFS['CIE 1931 2 Degree Standard Observer'],
                        reverse=False):
    """
    Returns the *dominant wavelength* :math:`\\lambda_d` for given colour
    stimulus :math:`xy` and the related :math:`xy_wl` first and :math:`xy_{cw}`
    second intersection coordinates with the spectral locus.

    In the eventuality where the :math:`xy_wl` first intersection coordinates
    are on the line of purples, the *complementary wavelength* will be
    computed in lieu.

    The *complementary wavelength* is indicated by a negative sign
    and the :math:`xy_{cw}` second intersection coordinates which are set by
    default to the same value than :math:`xy_wl` first intersection coordinates
    will be set to the *complementary dominant wavelength* intersection
    coordinates with the spectral locus.

    Parameters
    ----------
    xy : array_like
        Colour stimulus *xy* chromaticity coordinates.
    xy_n : array_like
        Achromatic stimulus *xy* chromaticity coordinates.
    cmfs : XYZ_ColourMatchingFunctions, optional
        Standard observer colour matching functions.
    reverse : bool, optional
        Reverse the computation direction to retrieve the
        *complementary wavelength*.

    Returns
    -------
    tuple
        *Dominant wavelength*, first intersection point *xy* chromaticity
        coordinates, second intersection point *xy* chromaticity coordinates.

    References
    ----------
    :cite:`CIETC1-482004o`, :cite:`Erdogana`

    Examples
    --------
    *Dominant wavelength* computation:

    >>> from pprint import pprint
    >>> xy = np.array([0.54369557, 0.32107944])
    >>> xy_n = np.array([0.31270000, 0.32900000])
    >>> cmfs = CMFS['CIE 1931 2 Degree Standard Observer']
    >>> pprint(dominant_wavelength(xy, xy_n, cmfs))  # doctest: +ELLIPSIS
    (array(616...),
     array([ 0.6835474...,  0.3162840...]),
     array([ 0.6835474...,  0.3162840...]))

    *Complementary dominant wavelength* is returned if the first intersection
    is located on the line of purples:

    >>> xy = np.array([0.37605506, 0.24452225])
    >>> pprint(dominant_wavelength(xy, xy_n, cmfs))  # doctest: +ELLIPSIS
    (array(-509.0),
     array([ 0.4572314...,  0.1362814...]),
     array([ 0.0104096...,  0.7320745...]))
    """

    xy = as_float_array(xy)
    xy_n = np.resize(xy_n, xy.shape)

    xy_s = XYZ_to_xy(cmfs.values)

    i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, xy_s, reverse)
    xy_cwl = xy_wl
    wl = cmfs.wavelengths[i_wl]

    xy_e = (extend_line_segment(xy, xy_n)
            if reverse else extend_line_segment(xy_n, xy))
    intersect = intersect_line_segments(
        np.concatenate((xy_n, xy_e), -1), np.hstack([xy_s[0],
                                                     xy_s[-1]])).intersect
    intersect = np.reshape(intersect, wl.shape)

    i_wl_r, xy_cwl_r = closest_spectral_locus_wavelength(
        xy, xy_n, xy_s, not reverse)
    wl_r = -cmfs.wavelengths[i_wl_r]

    wl = np.where(intersect, wl_r, wl)
    xy_cwl = np.where(intersect[..., np.newaxis], xy_cwl_r, xy_cwl)

    return wl, np.squeeze(xy_wl), np.squeeze(xy_cwl)
Пример #7
0
def dominant_wavelength(xy,
                        xy_n,
                        cmfs=CMFS['CIE 1931 2 Degree Standard Observer'],
                        reverse=False):
    """
    Returns the *dominant wavelength* :math:`\lambda_d` for given colour
    stimulus :math:`xy` and the related :math:`xy_wl` first and :math:`xy_{cw}`
    second intersection coordinates with the spectral locus.

    In the eventuality where the :math:`xy_wl` first intersection coordinates
    are on the line of purples, the *complementary wavelength* will be
    computed in lieu.

    The *complementary wavelength* is indicated by a negative sign
    and the :math:`xy_{cw}` second intersection coordinates which are set by
    default to the same value than :math:`xy_wl` first intersection coordinates
    will be set to the *complementary dominant wavelength* intersection
    coordinates with the spectral locus.

    Parameters
    ----------
    xy : array_like
        Colour stimulus *xy* chromaticity coordinates.
    xy_n : array_like
        Achromatic stimulus *xy* chromaticity coordinates.
    cmfs : XYZ_ColourMatchingFunctions, optional
        Standard observer colour matching functions.
    reverse : bool, optional
        Reverse the computation direction to retrieve the
        *complementary wavelength*.

    Returns
    -------
    tuple
        *Dominant wavelength*, first intersection point *xy* chromaticity
        coordinates, second intersection point *xy* chromaticity coordinates.

    See Also
    --------
    complementary_wavelength

    Examples
    --------
    *Dominant wavelength* computation:

    >>> from pprint import pprint
    >>> xy = np.array([0.26415, 0.37770])
    >>> xy_n = np.array([0.31270, 0.32900])
    >>> cmfs = CMFS['CIE 1931 2 Degree Standard Observer']
    >>> pprint(dominant_wavelength(xy, xy_n, cmfs))  # doctest: +ELLIPSIS
    (array(504...),
     array([ 0.0036969...,  0.6389577...]),
     array([ 0.0036969...,  0.6389577...]))

    *Complementary dominant wavelength* is returned if the first intersection
    is located on the line of purples:

    >>> xy = np.array([0.35000, 0.25000])
    >>> pprint(dominant_wavelength(xy, xy_n, cmfs))  # doctest: +ELLIPSIS
    (array(-520...),
     array([ 0.4133314...,  0.1158663...]),
     array([ 0.0743553...,  0.8338050...]))
    """

    xy = np.asarray(xy)
    xy_n = np.resize(xy_n, xy.shape)

    xy_s = XYZ_to_xy(cmfs.values)

    i_wl, xy_wl = closest_spectral_locus_wavelength(xy, xy_n, xy_s, reverse)
    xy_cwl = xy_wl
    wl = cmfs.wavelengths[i_wl]

    xy_e = (extend_line_segment(xy, xy_n)
            if reverse else
            extend_line_segment(xy_n, xy))
    intersect = intersect_line_segments(
        np.concatenate((xy_n, xy_e), -1),
        np.hstack((xy_s[0], xy_s[-1]))).intersect
    intersect = np.reshape(intersect, wl.shape)

    i_wl_r, xy_cwl_r = closest_spectral_locus_wavelength(
        xy, xy_n, xy_s, not reverse)
    wl_r = -cmfs.wavelengths[i_wl_r]

    wl = np.where(intersect, wl_r, wl)
    xy_cwl = np.where(intersect[..., np.newaxis], xy_cwl_r, xy_cwl)

    return wl, np.squeeze(xy_wl), np.squeeze(xy_cwl)
Пример #8
0
def closest_spectral_locus_wavelength(
        xy: ArrayLike,
        xy_n: ArrayLike,
        xy_s: ArrayLike,
        inverse: Boolean = False) -> Tuple[NDArray, NDArray]:
    """
    Return the coordinates and closest spectral locus wavelength index to the
    point where the line defined by the given achromatic stimulus :math:`xy_n`
    to colour stimulus :math:`xy_n` *CIE xy* chromaticity coordinates
    intersects the spectral locus.

    Parameters
    ----------
    xy
        Colour stimulus *CIE xy* chromaticity coordinates.
    xy_n
        Achromatic stimulus *CIE xy* chromaticity coordinates.
    xy_s
        Spectral locus *CIE xy* chromaticity coordinates.
    inverse
        The intersection will be computed using the colour stimulus :math:`xy`
        to achromatic stimulus :math:`xy_n` inverse direction.

    Returns
    -------
    :class:`tuple`
        Closest wavelength index, intersection point *CIE xy* chromaticity
        coordinates.

    Raises
    ------
    ValueError
        If no closest spectral locus wavelength index and coordinates found.

    Examples
    --------
    >>> from colour.colorimetry import MSDS_CMFS
    >>> cmfs = MSDS_CMFS['CIE 1931 2 Degree Standard Observer']
    >>> xy = np.array([0.54369557, 0.32107944])
    >>> xy_n = np.array([0.31270000, 0.32900000])
    >>> xy_s = XYZ_to_xy(cmfs.values)
    >>> ix, intersect = closest_spectral_locus_wavelength(xy, xy_n, xy_s)
    >>> print(ix) #
    256
    >>> print(intersect) # doctest: +ELLIPSIS
    [ 0.6835474...  0.3162840...]
    """

    xy = as_float_array(xy)
    xy_n = np.resize(xy_n, xy.shape)
    xy_s = as_float_array(xy_s)

    xy_e = (extend_line_segment(xy, xy_n) if inverse else extend_line_segment(
        xy_n, xy))

    # Closing horse-shoe shape to handle line of purples intersections.
    xy_s = np.vstack([xy_s, xy_s[0, :]])

    xy_wl = intersect_line_segments(
        np.concatenate((xy_n, xy_e), -1),
        np.hstack([xy_s, np.roll(xy_s, 1, axis=0)]),
    ).xy
    # Extracting the first intersection per-wavelength.
    xy_wl = np.sort(xy_wl, 1)[:, 0, :]

    if not len(xy_wl):
        raise ValueError(
            f"No closest spectral locus wavelength index and coordinates "
            f'found for "{xy}" colour stimulus and "{xy_n}" achromatic '
            f'stimulus "xy" chromaticity coordinates!')

    i_wl = np.argmin(scipy.spatial.distance.cdist(xy_wl, xy_s), axis=-1)

    i_wl = np.reshape(i_wl, xy.shape[0:-1])
    xy_wl = np.reshape(xy_wl, xy.shape)

    return i_wl, xy_wl