Exemple #1
0
def xyzr2xyz(xyzr: np.ndarray,
             *,
             axis: int = None,
             illuminant: Illuminant = None,
             observer: Observer = None) -> np.ndarray:
    """
    Convert normalized XYZ to LAB

    :param xyzr:
    :param axis: 
    :param illuminant: 
    :param observer: 
    :return: 
    """
    if axis is None:
        axis = get_matching_axis(xyzr.shape, 3)

    if illuminant is None:
        illuminant = get_default_illuminant()

    if observer is None:
        observer = get_default_observer()

    new_shape = tuple(-1 if dim == axis else 1
                      for dim in range(len(xyzr.shape)))
    white_point = illuminant.get_white_point(observer).reshape(new_shape)
    return xyzr * white_point
Exemple #2
0
 def __init__(
         self,
         data: ArrayLike,
         *,
         axis: int = None,
         illuminant: Illuminant = get_default_illuminant(),
         observer: Observer = get_default_observer(),
         rgbs: RgbSpecification = get_default_rgb_specification(),
         caa:
     ChromaticAdaptationAlgorithm = get_default_chromatic_adaptation_algorithm(
     ),
         is_scaled: bool = False):
     """
     
     :param data: the color space data to contain
     :param axis: the axis along which the color data lies. If `axis` is not
        specified, then it will be determined automatically by finding the 
        last dimension with the required size.
     :param illuminant: the illuminant
     :param observer: the observer
     :param rgbs: the rgb specification
     :param caa: the chromatic adaptation algorithm
     :param is_scaled: Whether or not the data is scaled
     """
     if is_scaled:
         self._data = np.array(data, copy=True) / self.scale_factor
     else:
         self._data = np.array(data, copy=True)
     self._data.flags.writeable = False
     self._axis = (axis if axis is not None else get_matching_axis(
         self._data.shape, 3))
     self._illuminant = (illuminant if illuminant is not None else
                         get_default_illuminant())
     self._observer = (observer
                       if observer is not None else get_default_observer())
     self._rgbs = (rgbs
                   if rgbs is not None else get_default_rgb_specification())
     self._caa = (caa if caa is not None else
                  get_default_chromatic_adaptation_algorithm())
     self._is_scaled = is_scaled
Exemple #3
0
def spectrum2xyz(
    spectrum: np.ndarray,
    wavelengths: np.ndarray,
    *,
    axis: int = None,
    illuminant: Illuminant = get_default_illuminant(),
    observer: Observer = get_default_observer()
) -> np.ndarray:
    """
    Convert reflectance spectrum to XYZ
    
    :param spectrum: the reflectance spectrum
    :param wavelengths: the wavelengths corresponding to the spectra
    :param axis: The axis along which the spectra lie. If this is None, 
        then the axis is the last axis with a size that matches wavelengths. 
    :param illuminant: the illuminant
    :param observer: the observer
    :return: 
    """
    if axis is None:
        axis = get_matching_axis(spectrum.shape, wavelengths.size)

    # Need to ensure that we reshape all the 1-D parts of the integral, so that
    # they can be broadcast properly
    new_shape = [1] * len(spectrum.shape)
    new_shape[axis] = -1
    wavelengths = wavelengths.reshape(new_shape)
    xbar = observer.get_xbar(wavelengths).reshape(new_shape)
    ybar = observer.get_ybar(wavelengths).reshape(new_shape)
    zbar = observer.get_zbar(wavelengths).reshape(new_shape)

    illuminant_psd = illuminant.get_psd(wavelengths).reshape(new_shape)

    norm_factor = (
        1 / integrate.trapz(illuminant_psd * ybar, x=wavelengths, axis=axis))

    phi = spectrum * illuminant_psd

    # This is the shape that each XYZ component must be
    component_shape = list(spectrum.shape)
    component_shape[axis] = 1

    def integrate_phi(bar: np.ndarray):
        area = norm_factor * integrate.trapz(bar * phi, wavelengths, axis=axis)
        # noinspection PyTypeChecker
        return np.reshape(area, component_shape)

    x = integrate_phi(xbar)
    y = integrate_phi(ybar)
    z = integrate_phi(zbar)
    return np.concatenate((x, y, z), axis=axis)
Exemple #4
0
def xyz2xyy(xyz: np.ndarray,
            *,
            axis: int = None,
            illuminant: Illuminant = None,
            observer: Observer = None) -> np.ndarray:
    """
    Convert XYZ to xyY
    
    :param xyz: 
    :param axis: 
    :param illuminant: 
    :param observer: 
    :return: 
    """
    if axis is None:
        axis = get_matching_axis(xyz.shape, 3)
    if illuminant is None:
        illuminant = get_default_illuminant()
    if observer is None:
        observer = get_default_observer()
    inds = construct_component_inds(axis, xyz.ndim, 3)

    denominator = np.sum(xyz, axis, keepdims=True)
    nzd = denominator != 0

    xyy = np.zeros(xyz.shape)
    if np.any(nzd):
        xyy[inds[0]][nzd] = xyz[inds[0]][nzd] / denominator[nzd]
        xyy[inds[1]][nzd] = xyz[inds[1]][nzd] / denominator[nzd]
    xyy[inds[2]] = xyz[inds[1]]
    if not np.all(nzd):
        # For any point that is pure black (X=Y=Z=0), give it the
        # chromaticity of the white point of the specified illuminant and
        # observer.
        white_point = illuminant.get_white_point(observer)
        # to prevent infinite recursion, ensure that the white point is
        # non-black
        if white_point[1] > 0:
            zd = np.logical_not(nzd)
            white_point_xyy = xyz2xyy(white_point,
                                      illuminant=illuminant,
                                      observer=observer)
            xyy[inds[0]][zd] = white_point_xyy[0]
            xyy[inds[1]][zd] = white_point_xyy[1]
    return xyy
Exemple #5
0
    def get_white_point(self, observer: Observer = None) -> np.ndarray:
        """
        Calculate the white point of the illuminant with a specified observer

        :param observer: The observer 
        :return: the white point
        """
        if observer is None:
            from chromathicity.defaults import get_default_observer
            observer = get_default_observer()
        wls = observer.wavelengths
        p = self.get_psd(wls)
        x_power = observer.xbar * p
        is_valid_x = np.logical_not(np.isnan(x_power))
        x_point = integrate.trapz(x_power[is_valid_x], wls[is_valid_x])
        y_power = observer.ybar * p
        is_valid_y = np.logical_not(np.isnan(y_power))
        y_point = integrate.trapz(y_power[is_valid_y], wls[is_valid_y])
        z_power = observer.zbar * p
        is_valid_z = np.logical_not(np.isnan(z_power))
        z_point = integrate.trapz(z_power[is_valid_z], wls[is_valid_z])
        return np.array([x_point, y_point, z_point]) / y_point
Exemple #6
0
def xyz2xyzr(
    xyz: np.ndarray,
    *,
    axis: int = None,
    illuminant: Illuminant = get_default_illuminant(),
    observer: Observer = get_default_observer()
) -> np.ndarray:
    """
    Convert XYZ to normalized XYZ reflectance
    :param xyz: the raw xyz values
    :param axis: the axis that the XYZ values lie along 
    :param illuminant: the illuminant
    :param observer: the observer
    :return: the xyz normalized Reflectance
    """
    if axis is None:
        axis = get_matching_axis(xyz.shape, 3)

    new_shape = [1] * len(xyz.shape)
    new_shape[axis] = -1
    white_point = illuminant.get_white_point(observer).reshape(new_shape)
    return xyz / white_point
Exemple #7
0
def xyz2lrgb(xyz: np.ndarray,
             *,
             axis: int = None,
             illuminant: Illuminant = None,
             observer: Observer = None,
             rgbs: RgbSpecification = None,
             caa: ChromaticAdaptationAlgorithm = None) -> np.ndarray:
    """
    Convert XYZ to linear RGB
    
    :param xyz: 
    :param axis: 
    :param illuminant: 
    :param observer: 
    :param rgbs: 
    :param caa: 
    :return: 
    """
    if axis is None:
        axis = get_matching_axis(xyz.shape, 3)
    if illuminant is None:
        illuminant = get_default_illuminant()
    if observer is None:
        observer = get_default_observer()
    if rgbs is None:
        rgbs = get_default_rgb_specification()
    if caa is None:
        caa = get_default_chromatic_adaptation_algorithm()

    # If the white points are not equal, we will need to convert to the
    # RGB white point
    source_white_point = illuminant.get_white_point(observer)
    destination_white_point = rgbs.white_point
    if not np.allclose(
            source_white_point, destination_white_point, rtol=1e-5,
            atol=1e-14):
        xyz = xyz2xyz(xyz, source_white_point, destination_white_point, axis,
                      caa)

    # Get the XYZ values into the correct shape for matrix multiplication
    n_dims = xyz.ndim
    new_dims = list(range(n_dims))
    if axis != n_dims - 1:
        new_dims[-1] = axis
        new_dims[axis] = n_dims - 1
    xyz = xyz.transpose(new_dims)

    input_shape = xyz.shape
    xyz_is_not_matrix = xyz.ndim != 2
    if xyz_is_not_matrix:
        xyz = xyz.reshape((-1, 3))

    # Convert to linear RGB
    m = rgbs.linear_transformation
    lrgb = np.linalg.solve(m.T, xyz.T).T

    # Transform the destination data back to the original shape
    if xyz_is_not_matrix:
        lrgb = lrgb.reshape(input_shape)

    return lrgb.transpose(new_dims)
Exemple #8
0
def lrgb2xyz(lrgb: np.ndarray,
             *,
             axis: int = None,
             illuminant: Illuminant = None,
             observer: Observer = None,
             rgbs: RgbSpecification = None,
             caa: ChromaticAdaptationAlgorithm = None) -> np.ndarray:
    """
    Convert from linear RGB to XYZ
    
    :param lrgb: 
    :param axis: 
    :param illuminant: 
    :param observer: 
    :param rgbs: 
    :param caa: 
    :return: 
    """
    if axis is None:
        axis = get_matching_axis(lrgb.shape, 3)
    if illuminant is None:
        illuminant = get_default_illuminant()
    if observer is None:
        observer = get_default_observer()
    if rgbs is None:
        rgbs = get_default_rgb_specification()
    if caa is None:
        caa = get_default_chromatic_adaptation_algorithm()

    # Get the XYZ values into the correct shape for matrix multiplication
    n_dims = lrgb.ndim
    new_dims = list(range(n_dims))
    if axis != n_dims - 1:
        new_dims[-1] = axis
        new_dims[axis] = n_dims - 1
    lrgb = lrgb.transpose(new_dims)

    input_shape = lrgb.shape
    lrgb_is_not_matrix = n_dims != 2
    if lrgb_is_not_matrix:
        lrgb = lrgb.reshape((-1, 3))

    # Do the transformation
    m = rgbs.linear_transformation
    xyz = lrgb.dot(m)

    # Transform back to the original shape
    if lrgb_is_not_matrix:
        xyz = xyz.reshape(input_shape)

    if axis != n_dims - 1:
        xyz = xyz.transpose(new_dims)

    source_white_point = rgbs.white_point
    destination_white_point = illuminant.get_white_point(observer)
    if not np.allclose(
            source_white_point, destination_white_point, rtol=1e-5,
            atol=1e-14):
        return xyz2xyz(xyz, source_white_point, destination_white_point, axis,
                       caa)
    else:
        return xyz