Determine most visible foreground color [duplicate] - c++

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 :-) ).

Related

how to find 16 most common colors in the img with extension BMP

for(int yy=0; yy<height/2; yy++)
{
SDL_Color kolor = getPixel(xx,yy); //we are gettig each pixel in img;
setPixel(xx+width/2,yy+height/2,kolor.r,kolor.g,kolor.b );
//setPixel(xx,yy+height/2,kolor.r,kolor.g,kolor.b);
//setPixel(xx+width/2,yy,kolor.r,kolor.g,kolor.b);
}
}
I am trying by using a loop find 16 most common colors in an img and get its RGB.
I've been using mapping and trying to do something with structure but everything was in avail.
If you have some ideas about how to find these colors, I'll be sorely grateful. Thanks
If you had a 4x4 img it would be simple.
Simplify likewise, histogram function each RGB level(0-255x3), expecting each "popular" color to be in there. Sort the mess into top 16 used for each, again expecting those to be correct. Unless you have wild color gyrations, nothing else.
Check second time to see if popular colors actually exist.
Last, you might want to group into "close enough" categories, jpegs are lossy to a fault, anything within 4 RGB variations is grouped together, reducing 0-256 values to 0-64, unless colors swing widely. If you've used paint program magic wand, you know tolerance=0 makes uber mistakes, same idea.
Median filter. It will definitely reduce color count by merging into an average messy color, sample a 3x3, 4x4, circular weighted sample area.
If all else fails, steal the 256 color safe web palette, and work from there.
SWAG-Scientific Wild Ass Guess method, good luck.

Smooth color transition algorithm

I am looking for a general algorithm to smoothly transition between two colors.
For example, this image is taken from Wikipedia and shows a transition from orange to blue.
When I try to do the same using my code (C++), first idea that came to mind is using the HSV color space, but the annoying in-between colors show-up.
What is the good way to achieve this ? Seems to be related to diminution of contrast or maybe use a different color space ?
I have done tons of these in the past. The smoothing can be performed many different ways, but the way they are probably doing here is a simple linear approach. This is to say that for each R, G, and B component, they simply figure out the "y = m*x + b" equation that connects the two points, and use that to figure out the components in between.
m[RED] = (ColorRight[RED] - ColorLeft[RED]) / PixelsWidthAttemptingToFillIn
m[GREEN] = (ColorRight[GREEN] - ColorLeft[GREEN]) / PixelsWidthAttemptingToFillIn
m[BLUE] = (ColorRight[BLUE] - ColorLeft[BLUE]) / PixelsWidthAttemptingToFillIn
b[RED] = ColorLeft[RED]
b[GREEN] = ColorLeft[GREEN]
b[BLUE] = ColorLeft[BLUE]
Any new color in between is now:
NewCol[pixelXFromLeft][RED] = m[RED] * pixelXFromLeft + ColorLeft[RED]
NewCol[pixelXFromLeft][GREEN] = m[GREEN] * pixelXFromLeft + ColorLeft[GREEN]
NewCol[pixelXFromLeft][BLUE] = m[BLUE] * pixelXFromLeft + ColorLeft[BLUE]
There are many mathematical ways to create a transition, what we really want to do is understand what transition you really want to see. If you want to see the exact transition from the above image, it is worth looking at the color values of that image. I wrote a program way back in time to look at such images and output there values graphically. Here is the output of my program for the above pseudocolor scale.
Based upon looking at the graph, it IS more complex than a linear as I stated above. The blue component looks mostly linear, the red could be emulated to linear, the green however looks to have a more rounded shape. We could perform mathematical analysis of the green to better understand its mathematical function, and use that instead. You may find that a linear interpolation with an increasing slope between 0 and ~70 pixels with a linear decreasing slope after pixel 70 is good enough.
If you look at the bottom of the screen, this program gives some statistical measures of each color component, such as min, max, and average, as well as how many pixels wide the image read was.
A simple linear interpolation of the R,G,B values will do it.
trumpetlicks has shown that the image you used is not a pure linear interpolation. But I think an interpolation gives you the effect you're looking for. Below I show an image with a linear interpolation on top and your original image on the bottom.
And here's the (Python) code that produced it:
for y in range(height/2):
for x in range(width):
p = x / float(width - 1)
r = int((1.0-p) * r1 + p * r2 + 0.5)
g = int((1.0-p) * g1 + p * g2 + 0.5)
b = int((1.0-p) * b1 + p * b2 + 0.5)
pix[x,y] = (r,g,b)
The HSV color space is not a very good color space to use for smooth transitions. This is because the h value, hue, is just used to arbitrarily define different colors around the 'color wheel'. That means if you go between two colors far apart on the wheel, you'll have to dip through a bunch of other colors. Not smooth at all.
It would make a lot more sense to use RGB (or CMYK). These 'component' color spaces are better defined to make smooth transitions because they represent how much of each 'component' a color needs.
A linear transition (see #trumpetlicks answer) for each component value, R, G and B should look 'pretty good'. Anything more than 'pretty good' is going to require an actual human to tweak the values because there are differences and asymmetries to how our eyes perceive color values in different color groups that aren't represented in either RBG or CMYK (or any standard).
The wikipedia image is using the algorithm that Photoshop uses. Unfortunately, that algorithm is not publicly available.
I've been researching into this to build an algorithm that takes a grayscale image as input and colorises it artificially according to a color palette:
■■■■ Grayscale input ■■■■ Output ■■■■■■■■■■■■■■■
Just like many of the other solutions, the algorithm uses linear interpolation to make the transition between colours. With your example, smooth_color_transition() should be invoked with the following arguments:
QImage input("gradient.jpg");
QVector<QColor> colors;
colors.push_back(QColor(242, 177, 103)); // orange
colors.push_back(QColor(124, 162, 248)); // blue-ish
QImage output = smooth_color_transition(input, colors);
output.save("output.jpg");
A comparison of the original image VS output from the algorithm can be seen below:
(output)
(original)
The visual artefacts that can be observed in the output are already present in the input (grayscale). The input image got these artefacts when it was resized to 189x51.
Here's another example that was created with a more complex color palette:
■■■■ Grayscale input ■■■■ Output ■■■■■■■■■■■■■■■
Seems to me like it would be easier to create the gradient using RGB values. You should first calculate the change in color for each value based on the width of the gradient. The following pseudocode would need to be done for R, G, and B values.
redDifference = (redValue2 - redValue1) / widthOfGradient
You can then render each pixel with these values like so:
for (int i = 0; i < widthOfGradient; i++) {
int r = round(redValue1 + i * redDifference)
// ...repeat for green and blue
drawLine(i, r, g, b)
}
I know you specified that you're using C++, but I created a JSFiddle demonstrating this working with your first gradient as an example: http://jsfiddle.net/eumf7/

Adding Colours (Colors) Together like Paint (Blue + Yellow = Green, etc)

I'm making an iOS game using cocos2d libraries.
Lets say you have two objects that have two separate colours - defined in RGB as
Blue: 0,0,255
Yellow: 255,255,0
I want to add blue and yellow to make green.
To over complicate things, let's say that the Blue object is bigger than the Yellow object (for the sake of argument let's say that the ratio is 2:1), I'm adding twice as much blue as yellow - how to I calculate this new (light green) colour correctly.
I understand LAB * Color Space is useful for this sort of 'natural colour' kind of thing, but I'm not sure how to use it - especially in the context of a cocos2d object which (AFAIK) is limited to using RGB in its colour schemes.
I'd really appreciate practical help on how to implement this. Thanks heaps!
21/4 Update: So in LAB* blue+yellow ≠ green (which makes sense when you see they're at opposite ends of the same channel). It's actually quite a tricky problem with a little bit of discussion on SO. It seems that the ultimate answer is to use the Kubelka-Munk method that a piece of open source software called Krita uses. I can't find that anywhere (either the formula or the code itself).
This question has a link which uses HSL to work in a similar method to paint. I'm going to try to see if it works, and I'll feed back the result here.
In the meantime if anyone knows how to implement Kubelka-Munk or where I can find code to do this, or another solution, I would be very, very stoked!
There is no color model where mixing blue and yellow makes green. Try it yourself with gouache, the only way it works is cyan and yellow. This is why you should try switching from RGB to CMYK, and back if you need. Here is how it's done
void toCMYK(float red, float green, float blue, float* cmyk)
{
float k = MIN(255-red,MIN(255-green,255-blue));
float c = 255*(255-red-k)/(255-k);
float m = 255*(255-green-k)/(255-k);
float y = 255*(255-blue-k)/(255-k);
cmyk[0] = c;
cmyk[1] = m;
cmyk[2] = y;
cmyk[3] = k;
}
void toRGB(float c, float m, float y, float k, float *rgb)
{
rgb[0] = -((c * (255-k)) / 255 + k - 255);
rgb[1] = -((m * (255-k)) / 255 + k - 255);
rgb[2] = -((y * (255-k)) / 255 + k - 255);
}
And then in your code, mix the cyan and yellow
float cmyk1[4];
toCMYK(255, 255, 0, cmyk1); // yellow
float cmyk2[4];
toCMYK(0, 255, 255, cmyk2); // cyan
// Mixing colors is as simple as adding
float cmykMix[] = { cmyk1[0] + cmyk2[0], cmyk1[1] + cmyk2[1], cmyk1[2] + cmyk2[2], cmyk1[3] + cmyk2[3] };
float rgb[3];
toRGB(cmykMix[0], cmykMix[1], cmykMix[2], cmykMix[3], rgb);
NSLog(#"RGB mix = (%f, %f, %f)", rgb[0], rgb[1], rgb[2]);
Running the code will yield: RGB mix = (0.000000, 255.000000, 0.000000)
Check the formulas on this site: http://www.easyrgb.com/index.php?X=MATH
I've been doing similar thing, and it can be achieved by converting RGB->XYZ->Lab. However the computation is quite expensive(if you doing it for a lot of pixels).
And forget about RGB math when trying to mix colors if you want to obtain results similar to human eye
I think, it is worth to try HSL color space. When adding colors we interpolate their Hue values (even taking in account objects weights). If the colors are 100% saturated, Luminance and Saturation values will be equal.
Dyes don't work in the real world quite like subtractive-color models suggests. The dyes used for CYMK printing are pretty close, since they're formulated for that purpose, but many dyes made from naturally-occurring substances can behave somewhat oddly. The difficulty is that while white light is perceived as a combination of red, green, and blue, it actually consists of many different wavelengths--literally "all the colors of the rainbow"--each of which will stimulate the red, green, and blue receptors in the eye by different amounts. It is possible for two colors which appear identical to in fact contain different combinations of wavelengths; likewise, two dyes may appear identical when viewed in white light, but absorb different combinations of wavelengths. Such dyes may look identical to each other when used alone, but may yield very different-seeming results when combined with something else.
Although dyes can sometimes be tricky, however, paints are even worse. Paints contain reflective particles, and some of the light which hits a painted surface will be reflected back off the surface by the first particle it hits; in that regard, they mix like additive colors. For example, if the paint contains 20% green particles, then a significant amount of green light will be reflected, regardless of what other colors it might contain. On the other hand, some of the light which hits a painted surface will bounce around and hit multiple particles. If any of those particles absorbs a photon of some color, that photon won't be reflected. In that regard, paints behave more like subtractive colors. In practice, paints behave somewhat like additive colors, somewhat like subtractive colors, and sometimes like something weird and wacky and totally unlike either.
Actually it seems that converting RGB->XYZ->LAB does exatly the same thing as RGB->LAB

Color detection algorithm - How should I do this?

I'm a bit stuck on designing a color detection system - I can't quite figure out a way to do it easily.
-
Basically, I have a library of images, that I want to sort by color. So if the user specifies 'sort by blue', then the most blue images will appear at the top of the results, with the least blue appearing at the bottom.
The problem is that the images aren't all one color, so it is doing two things at the same time:
1 - finding the bluest part of the image
2 - ranking this blue color (based on color hue and amount of this color).
I've tried about 3 or 4 different approaches, with varying results - none work well though, and 2 of these were quite mathematical algorithms (which all work much better on paper than in practice haha).
-
What different ways could I go about the whole process? I'm probably missing some really obvious ways it could work - any help or ideas would be much appreciated :)
-
EDIT: Thanks for all the responses - here's what I've tried so far:
getting the average rgb value for the whole image and comparing it to blue. Comparing was done using normalised rgb 3 space vectors and finding distances between them. This works the least well, an image with no blue could easily appear above an image with partial very strong blue.
finding the dominant color and comparing it to blue (again using 3 space vector distances). This didn't work as there might have been a large blue section of the image that wasn't the most (or in the top couple) of dominant color sections.
finding pixels that are close to blue, averaging all of these and comparing the answer to actual blue.
finding all the pixels that are close to blue, incrementing a count and finding a percentage based on count/total pixels.
Two thoughts come to mind:
Cheap version: convert images to HSV color space, and for each pixel compute cos(H - target_hue) or a reasonable approximation (for blue, target_hue would be 240 degrees), multiply by saturation, and average that quantity over all of the pixels in the image. High values are best. Note that colors that are closer to yellow than to blue have "negative blueness", and that black, white, and pure gray have equally "zero blueness". Note that you really want HSV, not HSL, in this situation, because the "S" in HSL doesn't map well to perceptual saturation. For example, the color #f8f8ff (RGB 248, 248, 255) has a saturation of 100% in HSL (i.e. a pure blue), but it looks nearly white. The same color in HSV has an "S" coordinate of only 3%, which is reasonable.
Less cheap version: convert images to CIELAB color space, discard L, and compute the distance in a*b* space between each pixel and the target color, then average or RMS over each pixel. Low values are best.
I think to measure "blueness" you'll need to take all three components into account, not just the blue. Just for example, [255,255,255] is pure white, not blue -- but [0, 0, 30] is pure blue, even though its blue component is much lower in value.
Alternatively, you could convert to something like HSL or HSV, in which case the "blueness" should be a bit simpler to measure (hue and saturation only).
I'd google for an algorythm for creating 256 colour palettes from 24bit images (see http://en.wikipedia.org/wiki/Color_quantization for more info) then see which colours in this palette dominate if the image was mapped to it. ie, running a tally for each 256 palette entry of how many pixels get mapped into it.
notes,
you of course don't need the whole 256, it's just saying 256 to help explain my thinking.
also by directly studying the algorythim for this palette generation might directly give you an answer.
Do you really need to find the bluest part of the image? Why not just rank the "blueness" of an image as the average blue-component value for all pixels?
Another possibility would be to find the density of pixels that pass a threshold, or minimum blue value necessary to qualify as a blue pixel.
If you have one pixel, I'd say its blueness in terms of RGB is the the value of B / (R + G + B), so 1 is totally blue and 0 is not blue at all and white is 1/3 blue. (Watch out for black, which is a special case.) And the blueness of an image is the average blueness of its pixels. And if that's too costly, just take the average of a fixed number of randomly-chosen pixels.
I would say to take the average of the RGB value itself over the whole picture. I would say that the pseudo below should give you the "average blue" of the picture.
SUMr
SUMg
SUMb
for pixel <- image
SUMr += pixel.r
SUMg += pixel.g
SUMb += pixel.b
SUMr / pixelcount
SUMg / pixelcount
SUMb / pixelcount
If this doesn't work out; then I would think that you would need to rank a "blue" pixel as being higher/lower weighted based on the G/B values. Then add up your weighted value(s) and compare those.
weight
for pixel <- image
tweight = b
b -= r
b -= g
b = 0 if b < 0
weight += tweight
compare weights of all images.

Recoloring an image based on current theme?

I want to develop a program which recolors the input image based on the given theme the same way as ms-powerpoint application does.
I am giving following link that shows what exactly i want to do.
I want to generate images same as images in below link under the Dark Variations and light Variations title based on the current theme.
http://blogs.msdn.com/powerpoint/archive/2006/07/06/658238.aspx
Can anybody give me idea,info regarding how to achieve it efficiently ??
You can give a look to the HSL colorspace to be able to have the same result. HSL means Hue, Saturation, Lightness.
You can keep the lightness of each pixel of your image and change only the hue. I think this will allow you to achieve what you want. You can find the RGB to HSL conversion on the wiki page.
Hope that helps.
Step 1: Choose the colors you want to represent black and white. For the dark variations, choose black and a light color; for the light variations, choose a dark color and white.
Step 2: Convert a pixel to gray. A common formula for this is L = R*0.3 + G*0.59 + B*0.11.
Step 3: Interpolate between the colors using the gray value. output.R = (L/255)*light.R + (1-(L/255))*dark.R and likewise for green and blue.
You can use a library like CxImage and convert the image to grayscale, then use the mix command with another image that you have made that is the same size as the original, and mix the two with the Mix command, using the filters. You can do mix-screen, and this should tint the pixels the color of the second image in the resultant image. Try playing with CxImage a bit, see if it will do what you want it to do. This is all coming off the top of my head, and its been a while since I have tried to do anything like this. YMMV, but this would be the simplest implementation. You could always look at how CxImage does the blend, and apply it to the image yourself.
I must say thanks to Mark and Patrice for ur guidance which helped me achieved it.
For light variation, I have done it by converting the theme colors to HSV colorspace and found relation between output color and theme color for black color (input) .
The relation was found to be linear for saturation and value and hue was almost constant.
I have used interpolation formula to make it generic for any given theme.
I have also make use of color matrix to achieve desired result.
Similarly for dark variation i have used white color as input and apply the same technique.