Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Would anyone be interested in gamut mapping? #311

Closed
crowsonkb opened this issue Mar 15, 2017 · 2 comments
Closed

Would anyone be interested in gamut mapping? #311

crowsonkb opened this issue Mar 15, 2017 · 2 comments

Comments

@crowsonkb
Copy link
Contributor

I have some rough, experimental code that finds the closest point in an RGB gamut to an out-of-gamut color, where 'closest' is defined by some distance metric i.e. CIEDE2000. It uses scipy.optimize.fmin_l_bfgs_b(), a optimizer which has support for simple box constraints.

Here is the code in its current rough state:

from functools import partial

import colour
import numpy as np
from scipy import optimize


def delta_e(rgb1, rgb2):
    """Returns the CIEDE2000 difference between rgb1 and rgb2 (both sRGB with range 0-1).
    Reference: https://en.wikipedia.org/wiki/Color_difference#CIEDE2000."""
    lab1 = colour.XYZ_to_Lab(colour.sRGB_to_XYZ(rgb1))
    lab2 = colour.XYZ_to_Lab(colour.sRGB_to_XYZ(rgb2))
    return colour.delta_E_CIE2000(lab1, lab2)


def opfunc_(x, loss, eps=1e-8):
    """Given a loss function i.e. delta_e(), returns the loss and gradient at a point x. The
    gradient is computed by finite difference."""
    grad = np.zeros_like(x)
    eye = np.eye(len(x))
    for i in range(len(x)):
        fx = loss(x)
        grad[i] = (loss(x + eps*eye[i]) - fx) / eps
    return fx, grad


def gamut_map(rgb):
    """Finds the nearest in-gamut color to an out-of-gamut color using delta_e() as its measure of
    difference."""
    x = np.clip(rgb, 0, 1)
    if (rgb == x).all():
        return x
    loss = partial(delta_e, rgb)
    opfunc = partial(opfunc_, loss=loss)
    x, _, _ = optimize.fmin_l_bfgs_b(opfunc, x, bounds=[(0, 1)]*3)
    return x
@KelSolaar
Copy link
Member

Hi @crowsonkb / Katherine,

I am definitely interested! :) This is a recurring convo topic, I had in mind to implement the ICC rendering intents (Absolute colorimetric, Relative colorimetric, Perceptual, Saturation) which was the purpose of this issue (even if empty): #154

I think we could stuff that in a new mapping sub-package at the root of colour.

@KelSolaar
Copy link
Member

@crowsonkb : Hey Katherine, any reason for closing the issue?

Cheers,

Thomas

@colour-science colour-science locked and limited conversation to collaborators Jan 16, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests

2 participants