Detect a colour based on its RGB - colors

I want to detect red and green objects from webcam. I am using this Trackingjs library. I think with the library, it will track the red, green and blue value of an object and detect the colour according to the built-in colour library in their code.
Their built-in colours are defined like this:
tracking.ColorTracker.registerColor('purple', function(r, g, b) {
var dx = r - 120;
var dy = g - 60;
var dz = b - 210;
if ((b - g) >= 100 && (r - g) >= 60) {
return true;
}
return dx * dx + dy * dy + dz * dz < 3500;
});
In the example above, objects which has their blue - green >= 100 and red -green >= 60 will be purple.
Can someone explain this to me how this works.
And if I want to detect red/ reddish and green/greenish, how should I do with the same function.

If you look at the colour RGB(120,60,210) it is this purple:
Now, dx measures the distance that the passed in colour is from the red component in that purple, dy measures the distance that the passed in colour is from the green component of that purple and dz measures the distance that the passed in colour is from the blue component.
The return value at the end, calculates, using 3-D Pythagorus, the square of the distance that the passed in colour is from purple in the 3-D colour cube with black at one corner, white diagonally opposite and R, G and B at the corners. So, it effectively defines a "sphere" of colour around the purple.
If you want to detect reddish tones, you will want something like
if ((r-g)>50 && (r-b)>50) { return true;}
which just says you want there to be more red than green and more red than blue.
If you want to detect greenish tones, you will want something like
if ((g-r)>50 && (g-b)>50) { return true;}
which just says you want there to be more green than red and more green than blue.

RGB Colour Parser.
this is what you need. And of course it can be used for color verification.
From the description:
A JavaScript class that accepts a string and tries to figure out a valid color out of it. Some accepted inputs are for example:
rgb(0, 23, 255)
#336699
ffee66
fb0
red
darkblue
cadet blue
DEMO: Demo

Related

Calculate a colour in a linear gradient

I'd like to implement something like the powerpoint image below. A gradient that goes between three values.
It starts at A (-1), the mid point is B (0), and the end is C (1).
I have realised that I can save some effort by calculating the 'start' as a-to-b, and the 'end' as b-to-c. I can do as 2 sets of 2 gradients, instead of 1 gradient with three values.
But I'm stumped (despite googling) on how to get from one colour to another - ideally in the RGB colour space.
I'd like to be able to have something like this -
const colourSpace = (value, startColor, endColor) => {...}
colorSpace(-0.25, red, yellow) // some sort of orangey color
colorSpace(1, yellow, green) // fully green
colorSpace(0.8, yellow, green) // mostly green
This isn't a front-end application, so no CSS gradients - which is what google was mostly referencing.
Thanks all,
Ollie
If you aren't too worried about being perceptually consistent across the color space (you would need to work in something like LAB mode to do that), you can just take the linear interpolation in RGB space. Basically you take a distance (between 0 and 1), multiply it by the different in the coordinates, and add it to the first one. This will allow you to find arbitrary points (i.e colors) along the line between any two colors.
For example between red and yellow:
let canvas = document.getElementById('canvas')
var ctx = canvas.getContext('2d');
let rgb1 = [255, 0, 0] // red
let rgb2 = [255, 255, 0] // yellow
function getPoint(d, a1, a2) {
// find a color d% between a1 and a2
return a1.map((p, i) => Math.floor(a1[i] + d * (a2[i] - a1[i])))
}
// for demo purposes fill a canvas
for (let i = 0, j = 0; i < 1; i += .002, j++) {
let rgb = getPoint(i, rgb1, rgb2)
ctx.fillStyle = `rgba(${rgb.join(",")}, 1)`
ctx.fillRect(j, 0, 1, 200);
}
<canvas id="canvas" width="500"></canvas>
You can repeat this to get multiple 'stops' in the gradient.
I ended up using Chroma for converting between colour spaces.

How to tell if a color is imaginary/impossible?

Short version
How can I tell if a color (e.g. XYZ) is impossible? (Wikipedia: Imposible color)
For example, this color is impossible:
XYZ: (15.96, 84.04, 0)
xyY: (0.1595, 0.8404, 0.8404)
Lab: (93, -196, 161) (D65 whitepoint)
It's impossible because it lies outside of the chromacity diagram:
How can I know that?
Incorrect code
The goal is for someone to fill in the function:
Boolean IsImaginaryColor(Single X, Single Y, Single Z)
{
//...TODO: Get someone to answer the question.
}
Right now we know that if any of the components of a corresponding LMS color are negative, then the color is imaginary.
That is a necessary, but not sufficient, condition for a color to be real. You can have all three components of LMS be positive, but it still be an imaginary color.
Boolean IsImaginaryColor(Single X, Single Y, Single Z)
{
//If any component of LMS color is negative,
//then the color is definitely imaginary.
LMSColor lms = XYZtoLMS(X, Y, Z);
if ((lms.L < 0) or (lms.M < 0) or (lms.S < 0))
return true;
//The color may still be imaginary,
//but i don't know how to solve that problem
//So as a first approximation i'll say it's real
return false;
}
LMSColor XYZtoLMS(Single X, Single Y, Single Z)
{
//perform Matrix multiplication:
//
// LMS = M * XYZ
//
// Where M is the M_CAT02 transformation matrix from CIECAM02
//
// 0.7328, 0.4296, -0.1624
// -0.7036, 1.6975, 0.0061
// 0.0030, 0.0136, 0.9834
LMSColor result;
result.L = 0.7328*X + 0.4296*Y + -0.1624*Z
result.M = -0.7036*X + 1.6975*Y + 0.0061*Z
result.S = 0.0030*X + 0.0136*Y + 0.9834*Z
}
In the xy color plane, this gives a good first-approximation (and nice visual indication) of impossible colors:
But the calculation still gives colors outside the chromacity diagram *(technically they're outside the "spectral locus"). So obviously only checking for negative components in LMS is incomplete.
Long Version
I am rendering a color picker. For example:
to pick an Lab color
you pick an ab color
for a given L plane
This is similar to what you can already do in Photoshop:
So in this case I've picked the color:
Lab: (72, -58, 119)
That color (assuming the D65 whitepoint) corresponds to the XYZ color:
Lab: (72, -58, 119)
XYZ: (25.22, 43.66, 0.36)
You can tell if a real color is outside the sRGB color gamut if one of its components is either:
less than 0
greater than 255
This XYZ color lies outside of the sRGB color space because one of it's components is negative:
XYZ: (25.22, 43.66, 0.36)
Lab: (72, -58, 119) (D65)
RGB: (106.1, 199.6, -234.7) (sRGB)
Photoshop already knows if a color is outside the sRGB color gamut, and will display a gumut warning:
But I'd like to go one step further
I can already know if a color is outside the sRGB color gamut.
But now i want to know if a color is imaginary, so i can continue to show the gamut, but hide completely impossible colors. A conceptual mockup might be:
Warning: I have no idea which of those colors actually are impossible. This is only the idea of the concept.
So what I need to know is if a color is impossible.
Background Theory - What is an example of an impossible color?
The Wikipedia page on Impossible colors notes that while the primaries for the sRGB color space all lie inside the spectral locus - and so are all real colors:
The ProPhotoRGB color space does use some primaries that are impossible:
The ProPhoto RGB color space uses imaginary green and blue primaries to obtain a larger gamut (space inside the triangle) than would be possible with three real primaries. However, some real colors are still irreproducible.
So now I have a concrete example of an impossible color: the green primary of the ProPhoto RGB color space:
| Color | CIE x | CIE y |
|-------|--------|--------|
| red | 0.7347 | 0.2653 |
| green | 0.1596 | 0.8404 | <--- this one
| blue | 0.0366 | 0.0001 |
| white | 0.3457 | 0.3585 |
This impossible color, given different color spaces, is:
xyY: (0.1596, 0.8404, 0.8404)
XYZ: (15.96, 84.04, 0)
LMS: (47.80, 131.43, 1.19)
Lab: (93.4679, -195.9973, 161.1515)
LCHab: (93.4679, 253.7415, 140.5725)
How can I tell that this color is impossible?
Given an XYZ color, how can I tell that it is impossible? E.g.:
XYZ: 15.96, 84.04, 0
Bonus Chatter
It's important to note the difference between
colors existing outside some gamut
and imaginary colors
A quick single-image primer would be:
Gamut: a color may not be displayable on your monitor, or printer, or phone, but it is still a real color - you could get a combination of Electromagnetic Waves of various wavelengths and intensities to generate the color
Imaginary: No combination of EM waves, of any intensities, of any wavelengths, can generate that response in the Long, Medium, and Short human cones
I already know how to tell if a color exists outside a particular color gamut.
I want to know if a color also exists outside the spectral locus.
In other words: i want to know if it is imaginary.
Bruce Lindbloom has a nice graphic that raises the issues of colors outside the Lab color space when you arbitrary choose to arbitrarily limit the a and b component values to +- 128:
Bonus Reading
https://physics.stackexchange.com/q/94375/
Determine that a Luv Color is non-imaginary
https://physics.stackexchange.com/questions/420614
This is a duplicate of the answer I gave here: Determine that a Luv Color is non-imaginary which relate to https://stackoverflow.com/a/48396021/931625
I think the safe way is to compute the XYZ volume boundaries and check if you are within or outside.

Changing radius/degree to x,y coordinate for output of a value at a point

I'm working on my final project for class, which is a program that displays the HSV values for a color and then allows the user to click on the color to display it and then choose a lightness value for it. There is a lot of code that I don't want to post here, so I've made a few pastebin posts and linked them here.
My friend/classmate made a circular color wheel, and I am trying to adapt his color selection method into a rectangular color spectrum so we can have both to display for the professor and write about the pros/cons, etc. of each.
My color displays correctly, but I am having trouble getting the color selection to work, and I have a sneaking suspicion that it has to do with this part:
def color_from_point(x, y, l):
degree = abs(degrees(atan(y / x)))
c = abs(x / cos(radians(degree)))
if x < 0 and y >= 0:
degree = (90 - degree) + 90
elif x < 0 and y < 0:
degree = degree + 180
elif x >= 0 and y <= 0:
degree = (90 - degree) + 270
if c > 255:
color = "White"
else:
color = colors(c / 255, degree, (l / 100))
return color
I am a complete beginner coder, and I'm not sure what to do to change this from using radius and degrees to using just the x,y coordinate to give me the color.
Here is the link to the pastebin post of the circular color picker of a friend/classmate:
http://pastebin.com/gAEg6rA1
Link to my WIP rectangular version:
http://pastebin.com/MJcqvvJw
Thanks in advance for any and all help!!

Given an RGB value, how do I create a tint (or shade)?

Given an RGB value, like 168, 0, 255, how do I create tints (make it lighter) and shades (make it darker) of the color?
Among several options for shading and tinting:
For shades, multiply each component by 1/4, 1/2, 3/4, etc., of its
previous value. The smaller the factor, the darker the shade.
For tints, calculate (255 - previous value), multiply that by 1/4,
1/2, 3/4, etc. (the greater the factor, the lighter the tint), and add that to the previous value (assuming each.component is a 8-bit integer).
Note that color manipulations (such as tints and other shading) should be done in linear RGB. However, RGB colors specified in documents or encoded in images and video are not likely to be in linear RGB, in which case a so-called inverse transfer function needs to be applied to each of the RGB color's components. This function varies with the RGB color space. For example, in the sRGB color space (which can be assumed if the RGB color space is unknown), this function is roughly equivalent to raising each sRGB color component (ranging from 0 through 1) to a power of 2.2. (Note that "linear RGB" is not an RGB color space.)
See also Violet Giraffe's comment about "gamma correction".
Some definitions
A shade is produced by "darkening" a hue or "adding black"
A tint is produced by "ligthening" a hue or "adding white"
Creating a tint or a shade
Depending on your Color Model, there are different methods to create a darker (shaded) or lighter (tinted) color:
RGB:
To shade:
newR = currentR * (1 - shade_factor)
newG = currentG * (1 - shade_factor)
newB = currentB * (1 - shade_factor)
To tint:
newR = currentR + (255 - currentR) * tint_factor
newG = currentG + (255 - currentG) * tint_factor
newB = currentB + (255 - currentB) * tint_factor
More generally, the color resulting in layering a color RGB(currentR,currentG,currentB) with a color RGBA(aR,aG,aB,alpha) is:
newR = currentR + (aR - currentR) * alpha
newG = currentG + (aG - currentG) * alpha
newB = currentB + (aB - currentB) * alpha
where (aR,aG,aB) = black = (0,0,0) for shading, and (aR,aG,aB) = white = (255,255,255) for tinting
HSV or HSB:
To shade: lower the Value / Brightness or increase the Saturation
To tint: lower the Saturation or increase the Value / Brightness
HSL:
To shade: lower the Lightness
To tint: increase the Lightness
There exists formulas to convert from one color model to another. As per your initial question, if you are in RGB and want to use the HSV model to shade for example, you can just convert to HSV, do the shading and convert back to RGB. Formula to convert are not trivial but can be found on the internet. Depending on your language, it might also be available as a core function :
RGB to HSV color in javascript?
Convert RGB value to HSV
Comparing the models
RGB has the advantage of being really simple to implement, but:
you can only shade or tint your color relatively
you have no idea if your color is already tinted or shaded
HSV or HSB is kind of complex because you need to play with two parameters to get what you want (Saturation & Value / Brightness)
HSL is the best from my point of view:
supported by CSS3 (for webapp)
simple and accurate:
50% means an unaltered Hue
>50% means the Hue is lighter (tint)
<50% means the Hue is darker (shade)
given a color you can determine if it is already tinted or shaded
you can tint or shade a color relatively or absolutely (by just replacing the Lightness part)
If you want to learn more about this subject: Wiki: Colors Model
For more information on what those models are: Wikipedia: HSL and HSV
I'm currently experimenting with canvas and pixels... I'm finding this logic works out for me better.
Use this to calculate the grey-ness ( luma ? )
but with both the existing value and the new 'tint' value
calculate the difference ( I found I did not need to multiply )
add to offset the 'tint' value
var grey = (r + g + b) / 3;
var grey2 = (new_r + new_g + new_b) / 3;
var dr = grey - grey2 * 1;
var dg = grey - grey2 * 1
var db = grey - grey2 * 1;
tint_r = new_r + dr;
tint_g = new_g + dg;
tint_b = new_b _ db;
or something like that...

RGB range for cold and warm colors?

What are ranges of cold and warm colors (http://www.diy-bathroom-remodel.com/images/color_wheel.gif) in RGB?
Taking the image you linked to as a reference of what is "warm" and "cold", we get the following.
The hottest colour is completely red (R=255, G=0, B=0).
The coldest colour is completely blue (R=0, G=0, B=255).
So the more R you have, the warmer the colour, and the more B you have, the cooler the colour. The G takes you through shades of turquoise, green, and yellow, each being increasingly warmer. When G is zero, you move in the lower left diagonal of the circle when R and B change. As G approaches 255, you cross over the upper right diagonal when R and B change.
However, the adjacent warm and cool colours have a different amount of green in them. The red immediately below the diagonal line has less green than the red immediately above it. The blue immediately below the diagonal line has less green than the blue immediately above it. This colour wheel looks like it doesn't represent the RGB space all that well especially in the blue end of the spectrum.
Ultimately, what you perceive as warm and cold depends on your monitor settings and light conditions. A lot of it is also your perception and cognition – what you think is warm or cold. In an image, the colour contrasts will affect your judgement. An "in-between" colour next to a clearly cool colour will look warm, but if the same colour is next to a clearly warm colour, it will look cool. Take a look at this colour wheel:
Here, every single 24-bit colour is shown (8 bits for each of R, G, and B), and the RGB values for the top, right, bottom, and left colours are shown. To my eye, there are more "cool" colours than "warm". Hope this helps you decide what the RGB values are for what you perceive as cool and warm colours.
Bonus: In the image above, you can also see the colours expressed in the hue, saturation, and value (HSV) colour model. Hue basically encodes the colour. Saturation encodes how "full" the colour is – less saturation makes it more "bleak", ie. white. Value encodes how dark it is – less value makes it more black, and also makes the white of the saturation more grey. Hue goes from 0 to 360 and corresponds to the angle of a circle, with 0 being totally red, 90 being a sort of neon green, 180 being bright blue, 270 being bluish purple, and 360 going back to red again. S and V go from 0 to 100 and will influence the exact shade – these examples are with S=V=100.
To my eye, H values in the ranges 0-80 and 330-360 are warm while H>80 and H<330 are cold (approximately).
You may want to open the colour selector in your favourite paint program. They can usually show HSV and RGB side by side and let you play with a colour wheel and see the effects on the HSV and RGB numbers. If you don't already have one, I would suggest GIMP. It has the colour wheel I've been using to get the numbers for this answer.
Bonus 2: All of the above assumes a subjective assessment of what is warm and what is cool. I think the only way to make an objective measurement of colour temperature is to measure it and express it on a temperature scale. Often Kelvin seems to be used. This measure takes into account not only shade of the colour, but its brightness (or how much light it emits) too. If you have a modern monitor, it probably has a temperature setting with some Kelvin values to choose from. It is possible to calculate the temperature of each RGB colour on your display, as long as you know the temperature of your white (the white balance). However, this calculation is very complex and I don't know how to do it. You may find Bruce Lindbloom a good source for further information, or any book on colour science. But I guess this is more than you bargained for with your question... ;-)
Looks to me like
if (B>R) {
color=cool
} else {
color=warm
}
is a pretty good approximation. You can check this out on your color wheel using:
http://www.colorzilla.com/firefox/
already answered but need to add this:
If you are interested in physical temperature and color dependencies then this is the scale you are looking for:
It is the color of light emitting from blackbody of given temperature. In astronomy is this used for star color classification (like BV color index).
It also can be used for indirect temperature measurements.
I've found these parameters satisfactory
For Warm color :
R from 128 to 255
G from 0 to 128
B from 0 to R
For Cold color :
R from 0 to B
G from 0 to 255
B from 128 to 255
Note that high green makes the color more cold (in my view but that's debatable ^^), that's why it's restricted for warm colors but not for cold ones.
Warm colors have more red,
and cool colors have more blue.
In RGB:
red = 0
blue = 0
if (red > blue) {
isWarmColor = true
isCoolColor = false
}
else if (red < blue) {
isWarmColor = false
isCoolColor = true
}
else {
isWarmColor = false
isCoolColor = false
}
In HSL:
hueAngle = 0
if (hueAngle < 0 || hueAngle > 360 || hueAngle == 90 || hueAngle == 270) {
isWarmColor = false
isCoolColor = false
}
else if (hueAngle > 90 && hueAngle < 270) {
isWarmColor = false
isCoolColor = true
}
else if (hueAngle < 90 && hueAngle > 270) {
isWarmColor = true
isCoolColor = false
}
Simple and accurate:
def is_hot(a):
return (a[..., 'RED'] > 128)
where 'RED' stands for the index of the red channel in the array a.
This works -- you can see for yourself by: (a) looking at the second wheel by Fabian Fagerholm and/or (b) go to the following link and move the bar up and down towards the two warm ends.
https://www.colorspire.com/rgb-color-wheel/
For me below calculation, with the inclusion of saturation, gives a better result. Since fully desaturated colors like Black, White and Gray are considered neutral colors.
if (hueAngle == 90 || hueAngle == 270 || (hueAngle == 0 && saturation == 0))
{
isWarmColor = false;
isCoolColor = false;
}
else if (hueAngle > 90 && hueAngle < 270)
{
isWarmColor = false;
isCoolColor = true;
}
else
{
isWarmColor = true;
isCoolColor = false;
}

Resources