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

Support arithmetic only for linear color spaces #7

Merged
merged 1 commit into from
Aug 25, 2015
Merged

Conversation

timholy
Copy link
Member

@timholy timholy commented Aug 24, 2015

As @jiahao has pointed out, arithmetic is complicated with Colors; in particular, nonlinear colorspaces should not be treated in a cavalier fashion. In Color.jl, we've had support for arithmetic through a more careful procedure: a+b -> original_colorspace(XYZ(a) + XYZ(b)), exploiting the fact that XYZ is, in fact, a vector space.

The problem with this seemingly-rational approach is that it fails due to gamut-correction. This was discovered while adding a "sanity-check" for the diagonal in the diagram that appears in https://github.com/JuliaGraphics/ColorVectorSpace.jl (@jiahao, I think you'll like that diagram). For example, one identity that should be satisfied in any colorspace is mean(a,a) == a, where mean(a,b) averages a and b. Let's see how we fare with Color.jl:

julia> using Color

julia> a = RGB(0.2,0,0)
Color.RGB{Float64}(0.2,0.0,0.0)

julia> 0.5*(a+a)
Color.RGB{Float64}(0.19999996068022607,1.1275668676820975e-7,0.0)   # works!

julia> a = RGB(0.8,0,0)
Color.RGB{Float64}(0.8,0.0,0.0)

julia> 0.5*(a+a)
Color.RGB{Float64}(0.735356922117825,1.8798490079085205e-6,0.0)    # oh, drat!

# compare:
julia> axyz = convert(XYZ, a)
Color.XYZ{Float64}(0.24905245040585275,0.12841771125364737,0.011674337386695216)

julia> convert(RGB, 0.5*(axyz+axyz))
Color.RGB{Float64}(0.799999934081552,1.0283349222330366e-6,0.0)    # works!

I think the only reasonable option is to disable arithmetic for any color type except those in a linear color space, and force users to do the conversion manually. OK with you, @jiahao?

I'm also CCing @glenn-sweeney or any other colorimetry experts, to ask whether my modification of sequential_palette is as intended. Or should we convert to XYZ before performing those operations?

@jiahao
Copy link
Contributor

jiahao commented Aug 24, 2015

I do like the diagram :) I'm pretty sure not understanding color space structure is the reason why very confused articles like this exist: http://www.theatlantic.com/technology/archive/2014/08/the-color-of-every-photo-on-the-internet-blended-together-is-orange/378614/

I think someone did point out that it is possible for addition by intermediate conversion into XYZ to produce a point outside the usual color gamut. Removing the vector space operations on nonlinear spaces seems like the only reasonable thing to do.

@m-lohmann
Copy link

Doing arithmetic in a (mathematically) linear color space (XYZ, LMS) makes physical/technical sense, like additive mixing of light sources etc.

The meaning in more or less _perceptually_ uniform color spaces like DIN, CIELAB etc. is different. You can do arithmetic on perceptually uniform color spaces as well, but then the result is only meaningful in a perceptual sense, e.g. finding a perceptually uniform gradient between two arbitrary colors.

I have to take a closer look at how out of gamut colors are handled, but as far as I remember, they are just clipped to [0, 1], which makes sensible gamut conversions or compressions impossible. You would need to be able to handle values outside this range to check for gamut problems or perform “proper” handling of out of gamut colors. Think of Photoshop rendering intents.

So, even if arithmetic is handled only in linear color spaces, there is still the problem of hvs vs. device dependent gamuts like sRGB and (the nonexistent?) AdobeRGB, for example.

I hope I understood the point of this properly.

I’ll try to look into it and maybe there is an easy way to overcome this problem.

@timholy
Copy link
Member Author

timholy commented Aug 24, 2015

Yes, these are good points. When you want to do arithmetic on the components of a color directly, you can of course do it. For example, here in this pull request (which I'm not sure is the right thing to be doing). But something as easy as a*x + b*y seems like something we should reserve only for vector spaces. If you come up with a good alternative, please do let us know!

@kmsquire
Copy link
Contributor

I actually disagree with this. In computer vision, metric space or not, people often measure "distance" between colors in RBG space. Of course it's wrong, but it's often good enough, and save translating to an actual metric space.

@m-lohmann
Copy link

What if there were a possibility to enable “illegal” operations by explicitely enforcing them via a parameter/switch, while keeping to legal operations by default?

@timholy
Copy link
Member Author

timholy commented Aug 24, 2015

In computer vision, metric space or not, people often measure "distance" between colors in RBG space. Of course it's wrong, but it's often good enough, and save translating to an actual metric space.

That's the whole reason ColorVectorSpace exists...that way Colors can be colorimetrically correct, and people who want fast performance for RGBs get that simply by saying using ColorVectorSpace.

@kmsquire
Copy link
Contributor

That's the whole reason ColorVectorSpace exists...

Okay, got it. I've been following along, but not closely enough to realize that's what this package was for. Cheers!

timholy added a commit that referenced this pull request Aug 25, 2015
Support arithmetic only for linear color spaces
@timholy timholy merged commit 1c43e5b into master Aug 25, 2015
@timholy timholy deleted the teh/arithmetic branch August 25, 2015 01:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants