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
Related
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.
I am having one image with illumination gradient. I need to do illumination correction before the thresholding. Is there any easy way to do the illumination correction in R or any easy maths? EBImage package doesn't have functionality for this.
One method is called flatfield correction. Basically, you multiply your image by :
correctionMatrix = mean(flatfieldImage) / flatFieldImage
where flatfield is an image with only the background (your gradient) and mean(flatfield) is the mean of your flatfield image (a scalar). If you do the element-wise division, correctionMatrix will be lower than 1 for high intensity background, and over 1 for lower intensity background. The multiplication between correctionMatrix and your image will have a fairly uniform background, and thresholding should work.
If you don't have access to a background image (your gradient only), then you can smooth your image enough to get only the gradient. It's less precise, but should work also.
A color can be represented as mixture of Red,Green and Blue.
Ex: (255,51,153)=pink
Is there an any good formula to get distinct colors by changing one variable?
such as (10x,22x,2x^2). So when x=1,2,3,4,.... It will give separate colors like Red,Green,Cyan,Blue.....etc.
Perhaps you'd be more interested in using HSL/HSV colors. Define the saturation and lightness and adjust the hue to get different colors. Check out the HSL and HSV wiki to learn more. A 15 to 30 degree adjustment of hue will result in a distinctly different color without messing with saturation or lightness.
An example of hsl in CSS is as follows.
<h1 style="color:hsl(0,50%,100%);">HSL Test</h1> //this will be red
The first value at 0 is red and advancing by 120 degrees will bring you to green and another 120 will bring you to blue and the last 120 will bring you back to red since the degree system is based on the 360 degrees of a circle. So 0 and 360 are the same just 60 & 420. The next two values are percentage based from 0% to 100% to define the intensity of that property. They're hard to explain so I made a quick fiddle that demonstrates this.
So to answer your question there is a good formula to adjust color it just depends on how exactly you want to change it. In the RGB world you can make things darker by lowering values uniformly and the opposite by heightening them. You can increase the different color presences by adjusting the individual color values as expected. However if you're trying to cycle the entire color wheel then this is difficult (although entirely possible) using RGB values. The real lesson to take away is that there are a number of ways to define a specific color and with each one different ways to traverse the spectrum. HSL and HSLA are very intuitive for many people since it's values don't really have to be guessed at. Pick a specific hue off the color wheel, Remember ROYGBIV as you imagine a value from 0-359. Define a saturation based on how bold you want the color to be and then a lightness based on how bright. It's far more useful then RGB in the large majority of cases as you'll see in that fiddle. Making a subset of the entire color spectrum with javascript only takes a few lines of code.
There is a similar question here
This javascript library can help you Name the Color Javascript Lib
A Demo of the library
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.
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.