-
Notifications
You must be signed in to change notification settings - Fork 82
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
Add Pointer's Gamut Check #317
Comments
Interesting. Right now the API is structured around the idea that there are color spaces, some of which have a restricted gamut. So, to check if a color is in gamut of a given space, you convert to that space and then use |
That's correct! |
The Pointer’s gamut is (an approximation of) the gamut of real surface colors as can be seen by the human eye, based on the research by Michael R. Pointer (1980). The linked library simply generates a mesh using hard coded points and Delauney's method and checks if a given point is within the mesh (using some threshold). The points come from documented tables, though I'd have to hunt around for the exact source. I don't think it is necessarily trivial to add though unless you depend on an external library to generate the mesh...I guess you could pre-generate mesh, then you just need an algorithm to resolve whether a point is within the mesh. You'd probably need a separate API method |
That would be less trivial than it seems to implement then, as it would require a restructing of the Color.js API itself to support this sort of thing. |
I'm sure you could special case |
I could, but it would be a code smell. Architecturally, For reference, this is the implementation of One solution would be to convert the // inGamut.js
const gamuts = {};
export { gamuts }; // in inGamut():
if ( spaceOrGamut in gamuts) {
return gamuts[spaceOrGamut](color, {epsilon});
} that other modules could import and add to: // pointers.js
import { gamuts } from "/path/to/src/space.js";
export default const pointers = gamuts.pointers = function(color, {epsilon} = {}) {
/* logic here */
} The downside of this is that a string argument could be either a color space id or a named gamut and you can't tell which one until runtime. Also, in theory they could conflict. As you can see, this is not simple at all! 😃 |
Thank you all for your input, it's much appreciated!
This illustration (from https://twitter.com/bjornornorn/status/1347633870343200770 ) better shows that even within sRGB there are colors that would be physically implausible. |
Pointer gamut data: https://www.rit-mcsl.org/UsefulData/PointerData.xls |
I imagine it is possible to use the Pointer Data to calculate an appropriate max chroma for a given lightness and hue in LCh (using the specified SC illuminant) for a given color and then compare it against that max chroma. Then you wouldn't need to build a pointer surface with a mesh. |
Yeah, it seems the easiest approach is to just compare the data within the data's provided model (LCh with the SC illuminant). Though, you could certainly transform the colors into an XYZ surface mesh and compare the points against that. The data represents the gamut at discrete lightness levels, so you can bisect to find the two closest lightness values and hue values and then interpolate the maximum chroma in that LCh (SC) space for the given color. Obviously, you need to also handle lightness that falls outside the gamut's boundary. No complicated mesh is needed. Resolving what the API looks like is probably the most complicated part. Red dots fall outside the gamut. Randomly generated points at the same lightness. EDIT: Fix bad lightness in title of images |
Pointer's gamut is an under-estimation of the gamut of physically realizable, non-fluorescent, non-emissive matt surface colors. Oh and for objects where the bidirectional reflectance distribution function (BRDF) is also flat (no dependence on angle of illumination) I forget the name now but there is another one which is an over-estimate, because it constrains all reflectance spectra to a step function with one transition (so full reflectance for part of the wavelength range and zero reflectance for the rest). That gives a theoretical limit. So an interesting functionality to have, but unrelated to the color.js gamut checks which are colorspace specific. |
Yeah, it is definitely not a color-specific gamut, and I came to the same conclusion that it should probably be something separate from the "gamut" check that exists in color.js currently. Anyways, it is pretty easy to implement. I have the code I used to implement it here (for anyone anxious to port it here): https://github.com/facelessuser/coloraide/blob/main/coloraide/gamut/pointer.py |
Possibly I should file a separate issue, but this is just to say: it would also be nice to be able to check if a color is in the visible gamut. @facelessuser from facelessuser/coloraide#333 it sounds like this is doable with a lookup table, as you've done with the Pointer's Gamut, although an algorithmic solution would be more precise? |
Yes, this would need a separate issue, but I don't think we have sufficiently granular info about the display to be able to tell… |
I know this is a bit of a niche use case, as it would be mainly used by CG artists.
Pointer's Gamut is based on a large dataset of colors found in the real world. By checking if a color is inside this gamut, you know as a CG artist that you are staying inside physically correct color values.
Here's a Python library that does this: https://github.com/colour-science/colour/blob/68342cb857c587eb5d5eb1df41e6d2e2666617f8/colour/volume/pointer_gamut.py#L32
Thanks,
Anton
The text was updated successfully, but these errors were encountered: