How to convert RGB code to 8 simple intervals (possible ?) - algorithm

I'm working on my Final Bachelor Project in Computer Science and for now I'm in a dead end.
Here is what I got stuck on:
I'm trying to classify any color (rgb code) in any of 8 (eight) simple colors.
In short terms I need to find 8 intervals where any colour can be placed and be considered a basic color (red, blue, green, black, yellow, purple, grey, brown ).
example: (18,218,23) to be classified as "green"
(81,,214,85) also "green"
but
(15,52,16) needs to be "black"
(110,117,110) needs to be "grey"
So there are 256 x 256 x 256 possible colors and I need to divide them in 8 (intervals) basic colors.
I'm waiting for some suggestions.
Cheers !
To be clear (as I've seen in comments) I'm looking for a particular set of 8 colors (red, black, green, brown, blue, purple, grey, yellow). Sorry for the orange above !

Don't do this in RGB, convert to a more convenient color space HSV is probably easiest - then the 8 "colors" are simply 8 intervals along the Hue axis.

Based on your example, I'd start with determining whether all components are approximately the same, or does on stand out. If they are about the same, then decide if the values are small enough to be black or not, then it's grey. If one value is different from the other two, then it is easy to check which is different and pick one of six possible colors accordingly.
Alternatively, set each component to either 0 or 1 according to a threshold, then you have 8 combinations to map to 8 colors.
threshold = 100:
(18,218,23) -> (0, 1, 0) - to be classified as "green"
(81,214,85) -> (0, 1, 0) - also "green"
(15,52,16) -> (0, 0, 0) - to be "black"
(110,117,110) -> (1, 1, 1) - to be "grey"

A simple solution is to do the distance from a defined points that you have labeled as a specific color. This is not a full proof solution, however it is an easy solution, and should work decently.
Long answer: Color spaces are annoying and don't translate well to labels.

RGB is horrible for this kind of thing, so your first step should be converting the color to a Lab color space. You can find many open implementations of this conversion online. Once you have your Lab color, everything becomes very simple. The three values become coordinates in three dimensions which you can easily section according to color - check out the images in the wikipedia link.

Don't know too much about Color scheme but calculating Euclidean distance would be a good solution.

Related

Superimpose red/green images in R using image() or rasterImage()

To highlight the difference between two identically sized matrices I would like to show the two superimposed in a semitransparent way using shades of red for the one matrix and shades of green for the other one (yielding yellow where they are identical) in R.
To display just one matrix I have
library(grDevices)
matr=replicate(10, rnorm(20,mean=0.5,sd=0.1))
colpalette=colorRampPalette(c("black", "red"))
image(matr^0.2,col = colpalette(1000),useRaster=T)
Does any one have any idea how I should adapt this to show two matrices matr1 and matr2 superimposed in red/green?
Also, what would be the best way to have a bit of control over the brightness & contrast of the resulting image? Are there better ways than the power transform I am using now?
cheers,
Tom
Ha just found an easy solution by first calculating the log2(difference) between the two matrices and plotting that using a palette with a break at zero. That makes sense, right?
library(grDevices)
matr1=replicate(10, rnorm(20,mean=0.5,sd=0.1))
matr2=replicate(10, rnorm(20,mean=0.5,sd=0.1))
matrdiff=log2(matr1/matr2)
nbcolors=1000
colpalette=colorRampPalette(c("red","yellow","green"))(nbcolors)
breaks = c(seq(min(matrdiff), 0, length.out=nbcolors/2), 0,
seq(0,max(matrdiff), length.out=nbcolors/2))
image(matrdiff,col=colpalette,breaks=breaks,useRaster=T)

Matlab restoring an image to its original colors

I want to take a picture of something.
The colors in the picture are not the same as I see in my eyes.
So in order to fix this problem, I decided to place a red paper (with RGB: [255 0 0]) and then take a picture including this paper.
If I see that the RGB of the paper is changed (such as [243 15 7]),
I will change all the RGB in the picture by the next way:
R (red), it will be added by value 12.
G (green), it will be subtracted by value 15.
B (blue), it will be subtracted by value 7.
By this way, my paper will be changed to his correct RGB [255 0 0] and then I can be sure that all the rest picture's RGB was changed to its original color.
What do you think about this way?
What you are trying to do is called Color Management/Color Correction.
I have some remarks:
First, you must make sure that your monitor is calibrated. If it isn't calibrated, it makes no sense to do a visual check. If you have a standard consumer monitor, chances are that you cannot calibrate it at all.
Why do you assume that the RGB of the paper is [255,0,0]? It could be slightly greener or bluer. You should use a known target, like Macbeth ColorChecker
The offset transformation that you are using, will not work if amount of light is changed. Instead, it should be multiplicative to be invariant to illumination intensity. Check out the standard method of color correction, Color Correction Matrix.
You will need more than one known color. Three is the absolute minimum to calibrate the matrix.
You can try to use white paper instead of red. By doing this, you will have information about three colors, not only red. In the perfect case, RGB values for white paper will be equal, for example, you get (197,197,197). But if they are not equal, for example (190, 204, 203), you can change them for each pixel by multiplying on some number:
mean = (190 + 204 + 203) / 3
red_new = red * mean / 190
green_new = green * mean / 204
blue_new = blue * mean / 203
i read about an iterative process of colour correction that could perhaps be applied in your case:
Correction with Photoshop in 7 Easy Steps by Helen
Bradley,
nevertheless, confirm that it works as expected
good luck

Value as colour representation

Converting a value to a colour is well known, I do understand the following two approaches (very well described in changing rgb color values to represent a value)
Value as shades of grey
Value as brightness of a base colour (e.g. brightness of blue)
But what is the best algorithm when I want to use the full colour range ("all colours"). When I use "greys" with 8bit RGB values, I actually do have a representation of 256 shades (white to black). But if I use the whole range, I could use more shades. Something like this. Also this would be easier to recognize.
Basically I need the algorithm in Javascript, but I guess all code such as C#, Java, pseudo code would do as well. The legend at the bottom shows the encoding, and I am looking for the algorithm for this.
So having a range of values(e.g. 1-1000), I could represent 1 as white and 1000 as black, but I could also represent 1 as yellow and 1000 as blue. But is there a standard algorithm for this? Looking at the example here, it is shown that they use colour intervals. I do not only want to use greys or change the brightness, but use all colours.
This is a visual demonstration (Flash required). Given values a represented in a color scheme, my goal is to calculate the colours.
I do have a linear colour range, e.g. from 1-30000
-- Update --
Here I found that here is something called a LabSpace:
Lab space is a way of representing colours where points that are close to each other are those that look similar to each other to humans.
So what I would need is an algorithm to represent the linear values in this lab space.
There are two basic ways to specify colors. One is a pre-defined list of colors (a palette) and then your color value is an index into this list. This is how old 8-bit color systems worked, and how GIF images still work. There are lists of web-safe colors, eg http://en.wikipedia.org/wiki/Web_colors, that typically fit into an 8-bit value. Often similar colors are adjacent, but sometimes not.
A palette has the advantage of requiring a small amount of data per pixel, but the disadvantage that you're limited in the number of different colors that can be on the screen at the same time.
The other basic way is to specify the coordinates of a color. One way is RGB, with a separate value for each primary color. Another is Hue/Saturation/Luminance. CMYK (Cyan, Magenta, Yellow and sometimes blacK) is used for print. This is what's typically referred to as true color and when you use a phrase like "all colors" it sounds like you're looking for a solution like this. For gradients and such HSL might be a perfect fit for you. For example, a gradient from a color to grey simply reduces the saturation value. If all you want are "pure" colors, then fix the saturation and luminance values and vary the hue.
Nearly all drawing systems require RGB, but the conversion from HSL to RGB is straight forward. http://en.wikipedia.org/wiki/HSL_and_HSV
If you can't spare the full 24 bits per color (8 bits per color, 32-bit color is the same but adds a transparency channel) you can use 15 or 16 bit color. It's the same thing, but instead of 8 bits per color you get 5 each (15 bit) or 5-6-5 (16 bit, green gets the extra bit because our eyes are more sensitive to shades of green). That fits into a short integer.
It depends on the purposes of your datasets.
For example, you can assign a color to each range of values (0-100 - red, 100-200 - green, 200-300 - blue) by changing the brightness within the range.
Horst,
The example you gave does not create gradients. Instead, they use N preset colors from an array and pick the next color as umbr points out. Something like this:
a = { "#ffffff", "#ff00ff", "#ff0000", "#888888", ... };
c = a[pos / 1000];
were pos is your value from 1 to 30,000 and c is the color you want to use. (you'd need to better define the index than pos / 1000 for this to work right in all situations.)
If you want a gradient effect, you can just use the simple math shown on the other answer you pointed out, although if you want to do that with any number of points, it has to be done with triangles. You'll have a lot of work to determine the triangles and properly define every point.
In JavaScript, it will be dog slow. (with OpenGL it would be instantaneous and you would not even have to compute the gradients, and that would be "faster than realtime.")
What you need is a transfer function.
given a float number, a transfer function can generate a color.
see this:
http://http.developer.nvidia.com/GPUGems/gpugems_ch39.html
and this:
http://graphicsrunner.blogspot.com/2009/01/volume-rendering-102-transfer-functions.html
the second article says that the isovalue is between [0,255]. But it doesn't have to be in that range.
Normally, we scale any float number to the [0,1] range, and apply transfer function to get the color value.

Is there any way to divide rgb color palette?

I'm trying to generate a color palette which has 16 colors.
i will display this palette in 4x4 grid.
so i have to find a way to rgb color palette which has
255*255*255 colors divided to 16 colors equally and logically.
i think it's gonna be a mathematical algorithm.
because i'm tring to pick 16 vectors from
3x3 matrix which picked in equal scale.
actually i have found a way depends on this "dividing color palette" problem.
i will use this color values with converting rgb values to hsv values.
hue, saturation, value
so i can use one integer value between 0-360 or i can use one integer between 0-100 (%) for my color palette.
finally, i can easily use this values for searhing/filtering my data based on color selection. i'm diving 0-360 range to 16 pices equally, so i can easily define 16 different colors.
but thanks for different approaches
You are basically projecting a cube (R X G X B) onto a square (4 X 4). First, I would start by asking myself what size cube fits inside that square.
1 X 1 X 1 = 1
2 X 2 X 2 = 8
3 X 3 X 3 = 27
The largest cube that fits in the square has 8 colors. At that point, I would note how conveniently 8 is an integral factor of 16.
I think the convenience would tempt me to use 8 basic colors in 2 variants like light and dark or saturated and unsaturated.
You can approach this as a purely mathematical equipartition problem, but then it isn't really about color.
If you are trying to equipartition a color palette in a way that is meaningful to human perception, there are a large number of non-linearities that need to be taken into account which this article only mentions. For example, the colors #fffffe, #fffeff, and #feffff occupy far corners of the mathematical space, but are nearly indistinguishable to the human eye.
When the number of selected colors (16) is so small (especially compared to the number of available colors), you'll be much better off hand-picking the good-looking palette or using a standard one (like some pre-defined system or Web palette for 16 color systems) instead of trying to invent a mathematical algorithm for selecting the palette.
A lot depends on what the colors are for. I you just want 16 somewhat arbitrary colors, I would suggest:
black darkgray lightgray white
darkred darkgreen darkblue darkyellow
medred medgreen medblue medyellow
lightred lightgreen lightblue lightyellow
I used that color set for a somewhat cartoonish-colored game (VGA) and found it worked pretty well. I think I sequenced the colors a little differently, but the sequence above would seem logical if arranged in a 4x4 square.
This is a standard problem and known as color quantization.
There are a couple of algorithms for this:
Objective: You basically want to make 16 clusters of your pixel in a 3 dimension space where the 3 axes varies from 0 to 255.
Methods are:
1) rounding of first significant bits. -- very easy to implement but does not give good result.
2) histogram method. - take median effort and give better result
3) quad tree. - state of the art data structure. Give best result but implementing the qaud tree data structure is hard.
There might be some more algorithms. But I have used these 3.
Start with the color as an integer for obvious math (or start with hex if you can think in base 16). Add to the color the number for each desired sample. Convert the color integer to hex, and then split the hex to RGB. In this code example the last color will be within the number of divisions to hex white (0xffffff).
# calculate color sample sizes
divisions = 16 # number of desired color samples
total_colors = 256**3-1
color_samples = int((total_colors) / divisions)
print('{0:,} colors in {1:,} parts requires {2:,} per step'.format(total_colors, divisions , color_samples))
# loop to print results
ii = 0
for io in range(0,total_colors,color_samples):
hex_color = '{0:0>6}'.format(hex(io)[2:])
rc = hex_color[0:2] # red
gc = hex_color[2:4] # blue
bc = hex_color[4:6] # green
print('{2:>5,} - {0:>10,} in hex {1} | '.format(io, hex_color, ii), end='')
print('r-{0} g-{1} b-{2} | '.format(rc, gc, bc), end='')
print('r-{0:0>3} g-{1:0>3} b-{2:0>3}'.format(int(rc,16), int(gc,16), int(bc,16)))
ii +=1

Good text foreground color for a given background color

I'm drawing a color selection button and I'm looking for a nice and simple formula to get a good text color (foreground) for a given background color in RGB.
A simple try would be to just take the complement color but this will produce an odd looking button for colors like pure blue or pure red.
Is there something well known that does this?
If it matters at all, I'm using QT.
For maximum legibility, you want maximum brightness contrast without getting into hues which don't work together. The most consistent way to do this is to stick with black or white for the text color. You might be able to come up with more aesthetically pleasing schemes, but none of them will be more legible.
To pick between black or white, you need to know the brightness of the background. This gets a little more complicated, due to two factors:
The perceived brightness of the individual primaries red, green, and blue are not identical. The quickest advice I can give is to use the traditional formula to convert RGB to gray - R*0.299 + G*0.587 + B*0.114. There are lots of other formulas.
The gamma curve applied to displays makes the middle gray value higher than you'd expect. This is easily solved by using 186 as the middle value rather than 128. Anything less than 186 should use white text, anything greater than 186 should use black text.
I'm no expert on programming things related to RGB, but from a designer's perspective, often the most readable color will be just a much lighter (if the background color is dark) or darker (if the background color is light) version of the same shade.
Basically you'd take your RGB values and if they're closer to 0 (dark) you'd push them each up by an equal amount for your foreground color, or vice versa if it's a light BG.
Complement colors can actually be really painful on the eyes for readability.
Leverage an outline for legibility
If by "good text color (foreground)" you intend it for legibility purposes when the user chooses any background colour, you can always produce white text having a black outline. It will be legible on any solid, patterned or gradient background, from black through white and anything in between.
Even if this doesn't hit the mark of your intention, I think it worthwhile posted here because I came looking for similar solutions.
Building on top of Mark's response, here's some Ruby code that'll do the work
rgbval = "8A23C0".hex
r = rgbval >> 16
g = (rgbval & 65280) >> 8
b = rgbval & 255
brightness = r*0.299 + g*0.587 + b*0.114
return (brightness > 160) ? "#000" : "#fff"
You are better off with a high difference in luminosity. In general, colored backgrounds with colored text suck for readability, hurting the eyes over time. Lightly tinted colors (e.g. in HSB, S~10%, B>90%) with black text work fine, or lightly tinted text over a black background. I'd stay away from coloring both. Dark text (b~30%, s>50%) with a subtle coloration over a white background can also be fine. Yellow (amber) text on a deep blue background has excellent readability, as does amber or green on black. This is why old dumbterms (vt100, vt52, etc.) went for these colors.
If you really need to do color-on-color for the 'look', you could reverse both H and B, while pinning saturation at a moderate to low level.
And one last note: if you have a 50% gray background, rethink your interface. You're robbing yourself of half your dynamic range! You're alienating low-visibility users, including anyone over 35...
Color combinations often look terrible when not carefully chosen. Why not use either white or black for the text, depending on the Brightness of the color. (Will need to convert to HSB first.)
Or let the user choose either black or white text.
Or use pre-defined combinations. This is what Google does in their calendar product.
I've been looking for a simailr answer and came across this post and some others that I thought I'd share. According to http://juicystudio.com/services/luminositycontrastratio.php#specify the "Success Criterion 1.4.3 of WCAG 2.0 requires the visual presentation of text and images of text has a contrast ratio of at least 4.5:1" with some exceptions. That site lets you put in foreground and background colors to compute their contrast, although it would be helpful if it would suggest alternatives or ranges.
One of the best sites I've found for visualizing color contrast is http://colorizer.org/ It lets you adjust almost all manner of color scales (RGB, CMYK, etc.) at the same time and then shows you the result on the screen, such as white text on a yellow background.
I usually look at color complements, they also have color complement wheels to help
http://www.makart.com/resources/artclass/cwheel.html
If your color is HSL, flip the Hue by 180 degrees for a decent calculation
I wanted to put #MarkRansom's answer into use and managed to create this snippet:
I got the values From seeing how sRGB converts to CIE XYZ and built upon that.
The script simply tracks the position of the foreground item and it's position regarding the colored background items.
Then based on background luminosity it gradually changes the foreground text color to either black or white.
Open the codepen for full example
https://codepen.io/AndrewKnife/pen/XWBggQq
const calculateLight = (colorItem: number) => {
let c = colorItem / 255.0;
if (c <= 0.03928) {
c /= 12.92;
} else {
c = Math.pow((c + 0.055) / 1.055, 2.4);
}
return c;
};
const calculateLuminosity = (color: RGBColor) => {
return (
0.2126 * calculateLight(color.r) +
0.7152 * calculateLight(color.g) +
0.0722 * calculateLight(color.b)
);
};
const getContrastColor = (color: RGBColor) => {
if (calculateLuminosity(color) > LUMINOSITY_LIMIT) {
return FONT_COLOR_DARK;
}
return FONT_COLOR_LIGHT;
};
I thing that converting to HSV might be the way, but IMO changing hue would look weird. I'd try keeping the hue and fiddling with value and maybe saturation (light red buttons with dark red text ... hm sounds scary :-) ).

Resources