Skip to content
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

Gamut mapping: clarification of chroma reduction algorithm #63

Closed
danburzo opened this issue Jan 11, 2021 · 9 comments
Closed

Gamut mapping: clarification of chroma reduction algorithm #63

danburzo opened this issue Jan 11, 2021 · 9 comments
Assignees
Labels
Topic: Algorithms Issues that relate to the actual color science

Comments

@danburzo
Copy link

danburzo commented Jan 11, 2021

Hello, I'm coming to the repo from @svgeesus's comment regarding color clamping in the CSSWG repository. I've finally had the chance to look into the proposed algorithm, and I just wanted to confirm some details.

A basic bisection method for reducing the LCH chroma:

let low = 0;
let high = color.chroma;
while (high - low > ε) {
	color.chroma = (high + low) / 2;
	if (displayable(color)) {
		low = color.chroma;
	} else {
		high = color.chroma;
	}
}

color.js compares the color at each iteration with its clipped version:

while ((high - low > ε) && (error < base_error)) {
	let clipped = mappedColor.toGamut({space, method: "clip"});
	let deltaE = mappedColor.deltaE(clipped, {method: "2000"});
	error = color.deltaE(mappedColor, {method: "2000"});
	if (deltaE - 2 < ε) {
		low = mappedColor[coordName];
		// console.log(++i, "in", mappedColor.chroma, mappedColor.srgb, error);
	}
	else {
		// console.log(++i, "out", mappedColor.chroma, mappedColor.srgb, clipped.srgb, deltaE, error);
		if (Math.abs(deltaE - 2) < ε) {
			// We've found the boundary
			break;
		}
		high = mappedColor[coordName];
	}
	mappedColor[coordName] = (high + low) / 2;
}

The current implementation seems to have diverged from the description in the docs:

Instead, the default algorithm reduces chroma (by binary search) and also, at each stage, calculates the deltaE2000 between the current estimate and a channel-clipped version of that color. If the deltaE is less than 2, the clipped color is displayed.

At a first pass through the function, what I see is the definition of displayable() / inGamut() relaxed to include colors at a deltaE2000 distance smaller than 2 to an in-gamut color (which, even by itself, sounds like a great idea). What I'm not seeing, unless I'm missing something, is how the condition Math.abs(deltaE - 2) < ε) can ever be fulfilled (and the clipped color returned early) without deltaE - 2 < ε catching it first.

@svgeesus
Copy link
Member

@danburzo just a confirmation that we are looking into it. Unfortunately, at the present time we have four findings:

  1. you are right, that condition can never trigger
  2. on the other hand, those edits did produce significantly better gamut mapping. We thought we knew why Now we don't
  3. There are some inefficiencies in the code, like calculating a clipped result, not using it, throwing it away, then recalculating it again and using it
  4. refactoring to remove the inefficiencies, use a consistent deltaE threshold, and remove the paths that can't ever be traversed, resulted in worse results.

So - perplexed, needs to be fixed, will continue to look at it.

@danburzo
Copy link
Author

Thanks for the update! I wanted to experiment a bit in this area, but I had to pick up some things and was unable to investigate further... I'll post back if I find anything interesting myself.

@svgeesus
Copy link
Member

Note that CSS Color 4 now describes gamut mapping. Much of that is just explanation, but it is followed by the actual algorithm which is constant-lightness, constant-hue chroma reduction in OKLCH using deltaE OK as the difference metric.

@danburzo
Copy link
Author

danburzo commented Mar 26, 2022

Hi @svgeesus, I'm only just now picking this up. I've started implementing the gamut mapping algo, and I have some feedback. Is this an appropriate venue for it, or shall I post it to csswg-drafts directly?

@svgeesus
Copy link
Member

I'm only just now picking this up. I've started implementing the gamut mapping algo, and I have some feedback. Is this an appropriate venue for it, or shall I post it to csswg-drafts directly?

If the feedback is about what CSS Color 4 defines (or, should define) for gamut mapping in CSS, please leave the feedback on the CSSWG repo.

If the feedback is specific to what color.js does - it will certainly offer the CSS algo as an option, probably as the default option, but can also offer other options - then this would be the best place.

see also w3c/csswg-drafts#7135 (comment) where a better option for CSS is being explored.

@LeaVerou LeaVerou added the Topic: Algorithms Issues that relate to the actual color science label Jun 16, 2022
@svgeesus
Copy link
Member

I would like to prototype the CSS Color 4 gamut mapping algorithm in color.js and have it available as an option. (It should probably the default, as it is clearly better than CIE LCH chroma reduction).

@LeaVerou
Copy link
Member

LeaVerou commented Feb 9, 2024

@svgeesus @danburzo what's the status of this? The CSS gamut mapping algorithm has been implemented in #344 and #352 , right?

@LeaVerou LeaVerou removed this from the Public release milestone Feb 9, 2024
@danburzo
Copy link
Author

danburzo commented Feb 9, 2024

It seems to me the current Color.js toGamut() implementation for the css method matches the pseudo-code from the latest css-color-4 ED.

In regards to the algorithm itself, I believe the binary search optimizations it bakes in are still slightly unintuitive, but since it’s functionally equivalent to the simplified “roughly in gamut” idea (as I point out here), it’s more of a nitpick at this point.

I consider the algorithm sufficiently clarified.

@LeaVerou
Copy link
Member

LeaVerou commented Feb 9, 2024

Thanks @danburzo, I’ll close this then but please do not hesitate to open a new issue if needed.

@LeaVerou LeaVerou closed this as completed Feb 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Topic: Algorithms Issues that relate to the actual color science
Projects
None yet
Development

No branches or pull requests

3 participants