How to replicate adding/mixing of HSV values in RGB space - math

At the moment I'm doing a colourizing effect using additive blending in HSV space. Have a diff value in HSV space which is added to an image texture's individual pixels to get the desired color effect. But this is turning out to be expensive as the fragment shader has to do two costly conversions to do the addition
RGB -> HSV
HSV addition
HSV -> RGB
Is there a better way to do this? The diff value will be provided in HSV only. And the final color representation is in RGB to draw.
Many Thanks,
Sak

You can get a similar effect to HSV manipulations by using a color matrix in RGB. For example, a rotation around the r=g=b axis is similar to a hue addition. (Adding x degrees in the hue channel is similar to a rotation of x degrees around r=g=b in RGB.) A translation along the r=g=b axis is similar to a value addition. (I believe that adding x to the value channel should be similar to adding x to all of r, g, and b.) And a uniform scale perpendicular to the r=g=b axis is similar to a saturation addition. I don't know off the top of my head the exact translation between adding x to saturation and scaling in RGB, but it shouldn't be too hard to work out. You should be able to compose these matrixes into a single matrix, and implement it as a single matrix multiply by the RGB value.

Related

Domain coloring (color wheel) plots of complex functions in Octave (Matlab)

I understand that domain or color wheel plotting is typical for complex functions.
Incredibly, I can't find a million + returns on a web search to easily allow me to reproduce some piece of art as this one in Wikipedia:
There is this online resource that reproduces plots with zeros in black - not bad at all... However, I'd like to ask for some simple annotated code in Octave to produce color plots of functions of complex numbers.
Here is an example:
I see here code to plot a complex function. However, it uses a different technique with the height representing the Re part of the image of the function, and the color representing the imaginary part:
Peter Kovesi has some fantastic color maps. He provides a MATLAB function, called colorcet, that we can use here to get the cyclic color map we need to represent the phase. Download this function before running the code below.
Let's start with creating a complex-valued test function f, where the magnitude increases from the center, and the phase is equal to the angle around the center. Much like the example you show:
% A test function
[xx,yy] = meshgrid(-128:128,-128:128);
z = xx + yy*1i;
f = z;
Next, we'll get its phase, convert it into an index into the colorcet C2 color map (which is cyclic), and finally reshape that back into the original function's shape. out here has 3 dimensions, the first two are the original dimensions, and the last one is RGB. imshow shows such a 3D matrix as a color image.
% Create a color image according to phase
cm = colorcet('C2');
phase = floor((angle(f) + pi) * ((size(cm,1)-1e-6) / (2*pi))) + 1;
out = cm(phase,:);
out = reshape(out,[size(f),3]);
The last part is to modulate the intensity of these colors using the magnitude of f. To make the discontinuities at powers of two, we take the base 2 logarithm, apply the modulo operation, and compute the power of two again. A simple multiplication with out decreases the intensity of the color where necessary:
% Compute the intensity, with discontinuities for |f|=2^n
magnitude = 0.5 * 2.^mod(log2(abs(f)),1);
out = out .* magnitude;
That last multiplication works in Octave and in the later versions of MATLAB. For older versions of MATLAB you need to use bsxfun instead:
out = bsxfun(#times,out,magnitude);
Finally, display using imshow:
% Display
imshow(out)
Note that the colors here are more muted than in your example. The colorcet color maps are perceptually uniform. That means that the same change in angle leads to the same perceptual change in color. In the example you posted, for example yellow is a very narrow, bright band. Such a band leads to false highlighting of certain features in the function, which might not be relevant at all. Perceptually uniform color maps are very important for proper interpretation of the data. Note also that this particular color map has easily-named colors (purple, blue, green, yellow) in the four cardinal directions. A purely real value is green (positive) or purple (negative), and a purely imaginary value is blue (positive) or yellow (negative).
There is also a great online tool made by Juan Carlos Ponce Campuzano for color wheel plotting.
In my experience it is much easier to use than the Octave solution. The downside is that you cannot use perceptually uniform coloring.

How to calculate RGB values with identical perceived brightness

I'm trying to generate RGB colors with the same perceived brightness.
The function R*0.2126+ G*0.7152+ B*0.0722 is said to calculate the perceived brightness (or equivalent grayscale color) for a given an RGB color.
Assuming we use the interval [0,1] for all RGB values, we can calculate the following:
yellow = RGB(1,1,0) => brightness=0.9278
blue = RGB(0,0,1) => brightness=0.0722
So, in order to make the yellow tone just as dim as the blue one i can simply perform this simple calculation on yellow for each of the RGB components:
dim_yellow = yellow * 0.0722 / 0.9278
However, when doing the opposite thing, thus "scaling" up the blue color to the same perceived brightness as the original yellow, the B component obviously exceeds 1, which cannot be displayed on a computer screen.
I guess the missing brightness from the excess B component could be "redistributed" to the R and G components, faking a brighter blue color. So what is the best general method to calculate those final RGB values?
THESE AREN'T THE MATHS YOU'RE LOOKING FOR
The function R*0.2126+ G*0.7152+ B*0.0722 is said to calculate the perceived brightness (or equivalent grayscale color) for a given an RGB color.
No this is incorrect, or at least incomplete. Yes, R*0.2126+ G*0.7152+ B*0.0722 are the spectral coefficients, but that is not the complete story.
First, Don't use the term brightness in this context. Brightness is not a measure of light, it is a perception, not a measurable quantity. When we are talking about light and colorimetry, use the term "luminance" (L or Y). Luminance is a linear measure of light, not perception.
Perceptual lightness, or L* (Lstar) from CIELAB, is based on human perception of changes in luminance. It is close to a power curve of about 0.43.
sRGB, the colorspace typically used for computer monitors and the web, is not linear like light, and it is also not exactly like the perceptual L* curve. sRGB's transfer curve is close to a 1/2.2 power curve. That is, the sRGB data/signal is raised to the power of 0.455, and then the monitor applies a power of 2.2.
WHAT'S BROKEN
Your math isn't working because you are not taking the transfer curves into account. You must linearize the sRGB values before applying the coefficients. Then the sum of these will equal a luminance of 1.
#FFFF00 in sRGB equals 0.9278 in luminance, but this is an sRGB value of 96.76% or an L* value of 97.14%
#0000FF in sRGB equals 0.0722 in luminance, but this is an sRGB value of 29.79% or an L* value of 32.3%
Here's a chart of some values, expanding on your example:
So to answer the rest of your question, to get a blue that matches a higher luminance than the monitor is capable of requires desaturating it, adding R and G to increase the lightness.
In this chart, we have the fully saturated but darker red and green to match the 7% blue luminance, then we have 18% luminance (as in an 18% grey card), and here we have to desaturate the blue to bring the luminance value up.
HOW TO CALC
First, you need to linearize the sRGB components, and THEN apply the coefficients, if you need to determine luminance. If you come up with some values doing math on linearized components, then you need to re-gamma encode to get back to sRGB.
I've discussed this is several other answers, such as this here.
I recommend you to use HSV color model instead of RGB since you can easily achive what you want only modifying Value(Brightness) component.
The wiki page also contains how to convert RGB to HSV and back
EDIT:
Try to use CIELAB color space since it approximate human's vision

Understanding color matrix of hue

This is a color matrix in ARGB:
[
R,0,0,0,0,
0,G,0,0,0,
0,0,B,0,0,
0,0,0,A,0,
0,0,0,0,1
]
And I know its hue color matrix below:
(
refers to gskinner's source code:
http://gskinner.com/blog/archives/2007/12/colormatrix_cla.html
)
define lr=0.213
define lg=0.715
define lb=0.072
define a=0.143
define b=0.140
define c=-0.283
define hueangle
define cos=cos(hueangle)
define sin=sin(hueangle)
[
lr+cos*(1-lr)+sin*(-lr),lg+cos*(-lg) +sin*(-lg),lb+cos(-lb) +sin*(1-lb),0,0,
lr+cos*(-lr)+ sin*(a),lg+cos*(1-lg)+sin*(b),lb+cos(-lb) +sin*(c),0,0,
lr+cos*(-lr)+ sin*(-(1-lr)),lg+cos*(-lg) +sin*(lg) ,lb+cos(1-lb)+sin*(lb),0,0,
0,0,0,1,0,
0,0,0,0,1
]
(Thanks son_of_fire for pointing out the inexplicitness of the questions in the last edition.)
Question
What formulas led to this hue matrix especially the numbers a,b and c in it?
lr, lg, and lb are the luminance constants. If you compute the dot product of <R, G, B> with <lr, lg, lb> you'll get the luminance of the color. This is useful if you want to change the hue and/or saturation without changing the luminance of the color.
I don't recognize a, b, and c off the top of my head. They're probably chrominance (saturation)-related.
The hueangle is the angle you want to rotate your color. It rotates around the R=G=B axis. If you rotate 180° red becomes cyan, green becomes magenta, yellow becomes blue, etc.
You might find the Gamma FAQ useful, as it describes a lot of these concepts. And this page describes the math behind rotating around an arbitrary axis.
Also, you generally want a 4x4 matrix for color conversion, not 5x5.

why do I not find a LAB color cube?

I use the R colorspace package to convert a three-dimensional point into a LAB color. The LAB color is defined with three coordinates, the first one ranges from 0 to 100 and the two other ones range from -100 to 100.
But searching with Google I do not find a cuboidal representation of the LAB color space. Why ?
Short answer
The LAB color space, a.k.a. gamut, contain colors that are impossible to reproduce in nature or on a screen (according to this page).
Elaboration on converting RGB to LAB
I guess the reason you ask is that you want to make some kind of printed material and want to be sure the colors turn out right. I am merely an enthusiastic amateur in this field, but think this paragraph from the wikipedia article on lab color space explains some of the complications.
There are no simple formulas for conversion between RGB or CMYK values
and L*a*b*, because the RGB and CMYK color models are device
dependent. The RGB or CMYK values first need to be transformed to a
specific absolute color space, such as sRGB or Adobe RGB. This
adjustment will be device dependent, but the resulting data from the
transform will be device independent, allowing data to be transformed
to the CIE 1931 color space and then transformed into L*a*b*.
That is, in order to create a lab color cube, you must first find the transformation from your monitor specific color space into absolute color space. This is surprisingly difficult since the mapping is not linear or on any other simple form. The transformation is not likely to be perfect either since the RGB and LAB spaces do not span the same subspace (speculating here). I once talked to a printmaker about this and he said altough the human eye only has 4 types of color receptors (RGB + light intensity) you need about 17 color components on generate the full spectrum of visible colors on paper. Both RGB and LAB compromises on that, optimized for different purposes.
Bottom line
You can calibrate your screen to set up the transformation needed to convert the RGB of the screen to the LAB colors of human eyes, and then go on to make a color cube. However, it will only apply to your very monitor and not be perfect. You are best off test printing different color profiles and choose the one you like best.
Because there is no such thing. The CIELAB colour space has a Cartesian representation (of infinite size), but the (finite) gamut that we can perceive is not cubic, it has a complicated shape. Varying the two coordinates a* and b* independently in a pre-defined range may seem convenient, but this is fundamentally not the way human perception works.

how to scale a Bezier curve (using HTML5 canvas)?

I have a Bezier path stored as an array of several points, which is each an array of coordinates in the form [cp1x,cp1y,cp2x,cp2y,x,y].
I'd like to be able to scale this path up and down to adjust its size, but I don't know the math to do that. I tried applying a coefficient to each of the coordinate values, but that didn't seem to work.
Does anybody know how to achieve this?
In the standard representation, the points P, represent actual points in space, so you'd move them around like any other points. That is to scale them, just multiple everything by you scaling factor: say it's a, so that would be [a*cp1x,a*cp1y,a*cp2x,a*cp2y,a*x,a*y], or if you want to scale x and y separately, you can use different factors for the x and y components.
Note also that this will scale things relative to the origin (x=0, y=0), so if you don't have any curves at the origin, it can look like a shift. If you want to negate the effect of this shift you can subtract Px and Py from the x and y values respectively, where Px and Py is the point you want to not move when scaled, before you do the scaling (and then add it back after you multiple, if you want to). But if what you want to do is scale an entire canvas, like going from 5x5 inch to 7x7, you'd want to do the multiplication without any shifts (and in this case, by 7./5).

Resources