C# get good color for label - c#

I am using a random color for the background in my Windows Forms application. Now I want to display a label.
The problem is that when the random color is white and the label is too, then the label is not visible.
How can I get a perfect color that is visible on my background color? (My background color is a random color from System.Drawing.Color.)

There are various ways to ensure a proper contrast.
Option one : I usually stick to keeping the text Black or White, depending on the brightness of back color.
To get the brightness one could simply go for the built-in function Color.GetBrightness()
Unfortunately this is not really a good solution, as the result is not perceptually correct; to wit: Green and Yellow have the same values, which is obviously not what our eyes will perceive.
Instead this tiny function will help:
float getBrightness(Color c)
{ return (c.R * 0.299f + c.G * 0.587f + c.B *0.114f) / 256f; }
Now we can pick either Black or White:
Label lbl = new Label();
lbl.BackColor = colors[rnd.Next(colors.Count)];
lbl.ForeColor = getBrightness(lbl.BackColor) < 0.55 ? Color.White : Color.Black;
The code uses a list of known colors:
List<Color> colors = ((KnownColor[])Enum.GetValues(typeof(KnownColor))).
Select(x => Color.FromKnownColor(x)).ToList();
Option two : If you want to get colors in the foreground you could pick it randomly and repeat until you get a decent contrast by comparing e.g.
while (Math.Abs(c1.GetBrightness() - c2.GetBrightness()) < 0.5f )
c2 = colors[rnd.Next(colors.Count)];
Note that you must not push the epsilon value too high or else it won't find a suitable color. This happens when trying to find a color that is too far away from a medium brightness! You could add a counter and after a while pick simply black or white..
Option three : Yet another way would be to construct a color with Color.FromArgb().
You could start by inverting each channel, which will give nice color contrasts; but if the color one is of medium brightness and/or saturation you would have to correct, maybe again by picking black or white..
Note: for the above image I have enumerated all KnownColors, which already looks pretty random.
To add some order you may sort the list by color properties, e.g. by hue, then by brightness:
List<Color> allcolors = ((KnownColor[])Enum.GetValues(typeof(KnownColor)))
.Select(x => Color.FromKnownColor(x))
.OrderBy(x => x.GetHue()).ThenBy(x => getBrightness(x)).ToList();

Related

Is this a standard color palette?

I am doing some reverse engineering work on a program. One of the things I am trying to pull out of the old data is a color that was chosen from the below color pallet
The way the old software is refrencing the color is by the index in the pallet (so 0 is white, 1 is yellow, 2 is orange, and so on). Is the above pallet a standard layout of some type?
What my best hope is to find some class built in to .NET where I could pass the same index number in and get the color back, however I don't have high hopes for finding something that nice.
Besides using paint and an eyedropper to manually map out the whole table is there any option to make this easier on me?
You can write some code that reads that bitmap and examines the pixels to build the palette of colors in the bitmap you've extracted.
This is a 16 x 16 = 256 palette. The old Software possibly stored this palette in a gif-file. You could build an array of hex values from this palette (hard coded or at runtime).
The first row is a "useful"-colors row.
Row two to eight shifts hue values from hue 338° to hue 335°.
Row nine to fifteen show (7) tints and (8) shades (HSB color model) of hue 0°, 30°, 60°, 116°, 180°, 230° and 300°.
Last row is obviously a grayscale.
I don't think this is a standard layout. If you want an exact value, you need to use the eyedropper...
I ended up using SLaks suggestion and just looped over the image and read the pixel values from the center of each square. Here is quick proof of concept test I ran that loaded all of the colors in to a TableLayoutPanel and it worked perfectly.
private void button1_Click(object sender, EventArgs e)
{
string pngPath = #"E:\Color Pallet.png";
tableLayoutPanel1.Controls.Clear();
using (var bitmap = new Bitmap(pngPath))
{
for (int i = 0; i < 256; i++)
{
var color = bitmap.GetPixel(5 + (10*(i%16)), 5 + (10*(i/16)));
tableLayoutPanel1.Controls.Add(new Panel {Dock = DockStyle.Fill, BackColor = color}, i % 16, i / 16);
}
}
#SLaks, if you post your own answer I will delete mine and accept yours.

Calculate a middle color from two colors (from a gradient)

I my WPF application, I have two color object.
I would like to calculate the "middle" color from my two colors.
My two color are from a gradient and I would like to find the middle color.
Can anyone help me calculate this ?
Assuming you have the RGB values for the colours, you can do this by linearly interpolating them. If the original colours are (R1, G1, B1) and (R2, G2, B2) the colour in between them can be found by averaging: ((R1 + R2) / 2, (G1 + G2) / 2, (B1 + B2) / 2).
(Assuming you're only looking for the colour in the middle - if you're looking to calculate the whole gradient, use linear interpolation on each component.)

Color Mixing for more than 2 colors

I'm trying to find an algorithm that will mix colors, based on a % amount. I'm working on an election project, and want to be able to assign each candidate in a race a different color, and then create a "resulting" color based on how much of the vote that candidate has gotten.
This question:
Is there an algorithm for color mixing that works like mixing real colors?
is close the question I'm asking - but potentially I need to be able to mix 3 or 4 or 5 colors together. I understand that this ultimately an incredibly complicated question - but I'm curious what the suggested method is for doing this on more than 2 colors. So, I might have:
Red (40%), Green(10%), Blue (50%)
OR
Red (40%), Yellow (5%), Blue (10%), Orange (45%)
Once again, I know that the last example would probably produce some sort of Gray or Brown or Black - and that's fine. My problem is that I don't even have an algorithm that attempts to solve this problem.
Edit:
So - after posting this message, I realized that what I was really trying to accomplish was basically PAINT color mixing. IN other words, I don't want LIGHT color mixing, I want to simulate what would happen with PAINT mixing - as that is the "predictable" result that I kept 'expecting' - and was having trouble getting.
I found this link: http://painting.about.com/library/blpaint/blcolormixingpalette1.htm
which behaves VERY closely to what I'm trying to accomplish - and exposes a small "flaw" in my original idea. What is demonstrates is that even though I can mix up to 6 colors using this algorithm, the actual "data" is always broken down into the 3 primary colors.
Even so - this is very close.
Thank you to everyone who has contributed to this thread - all of these suggestions have been very helpful in the exploration of this surprising complex question/field.
Have you tried simply taking a weighted sum?
d.R = a.R*0.25 + b.R*0.25 + c.R*0.5
d.G = a.G*0.25 + b.G*0.25 + c.G*0.5
...
where a, b, c are the colors you're mixing
From my research, it seems there isn't any one correct answer, but whatever seems to produce the most appropriate effect. Moreover, I don't think simply blending the colors would really indicate anything on an infograph. You'd be better off trying something like a really granular dither http://en.wikipedia.org/wiki/Dither#Digital_photography_and_image_processing
The RGB colors system is bad for such calculation, because it does not work like we humans perceive colors. For example we just do not perceive yellow as a mixture of green and red.
The first step is to convert your colors in a color space where you have a lightness coordinate and two coordinates for the color.
This can be either HLS or CIELAB. HLS conversion is more simply, but coordinates in CIELAB represent better the human color experience. Equal distances in this color space are at least roughly equal distances in human perception.
In this color space you have coordidates L, A and B. L should be the same for all your colors. Your base colors should be on a circle around the gray-point, so they have the same distance, and no colors is stronger than the others. so you basically have a few points in the plane and need to calculate their weighted middle point of these points. The you convert this point back to RGB.
It all depends on the primary colors in your color model. It looks like you're using the RGB color model which means that all colors can be represented by a mix of red, green, and blue.
That said, any colors that are not red, green, or blue you would decompose those down into their red, green and blue counterparts, and then multiply those weights by the weight overall.
In your second example:
Red (40%), Yellow (5%), Blue (10%), Orange (45%)
Yellow and Orange need to be decomposed. Assuming you have the Color instance for orange, you can do this:
orangeRed = o.R * .05;
orangeBlue = o.B * .05;
orangeGreen = o.G * .05;
This can actually be generalized across all Color instances (for primary colors, only one of R, G and B will have a value) and then sums and weighted averages can be taken to get your mixed color.
An easy extension method that does this would be:
static Color Mix(this IEnumerable<Tuple<Color, double>> colors)
{
// The red, green, and blue values.
double red = 0.0, green = 0.0, blue = 0.0;
// The denominator for the weighted average for a color.
double denominator = byte.MaxValue * 3.0;
// Cycle through the colors.
foreach (Tuple<Color, double> color in colors)
{
// Get the weighted average value for each component, and then
// multiply that by the weight for the color.
red += ((color.Item1.R / denominator) * color.Item2);
green += ((color.Item1.G / denominator) * color.Item2);
blue += ((color.Item1.B / denominator) * color.Item2);
}
// Create the color and return.
return Color.FromArgb((int) red, (int) green, (int) blue);
}

XNA BlendState with SpriteBatch

We are needing a BlendState to act as the following:
Transparent PNGs are drawn as expected, with anything behind them preserved
We use Color.White to draw a PNG as-is
We will change the alpha channel of the color to change the "opacity" of the texture
To get this effect, BlendState.AlphaBlend is close, but draws white as the transparent part if we set alpha to 100 or any number other than 255.
So we attempted this:
_blendState = new BlendState();
_blendState.AlphaSourceBlend = Blend.SourceAlpha;
_blendState.AlphaDestinationBlend = Blend.InverseSourceAlpha;
_blendState.ColorSourceBlend = Blend.SourceAlpha;
_blendState.ColorDestinationBlend = Blend.InverseSourceAlpha;
This works, except we now get undesired effects if two PNGs are on top of one another. Basically we get some weird lines where it looks like pixel data is being added (or something).
Example:
Effectively, BlendState.AlphaBlend is this:
_blendState = new BlendState();
_blendState.AlphaSourceBlend = Blend.SourceAlpha;
_blendState.AlphaDestinationBlend = Blend.InverseSourceAlpha;
_blendState.ColorSourceBlend = Blend.One;
_blendState.ColorDestinationBlend = Blend.InverseSourceAlpha;
Image looks better than above:
But then alpha doesn't work, using 100 as alpha would replace the background with white.
What BlendState should we use to get our desired effect from SpriteBatch? We are OK to use a different color such as Color.Black there is another way to get it to work.
*PS - another nice feature would be if we could use Color.Red to "tint" a texture, but we want it to work in general first.
Try setting premultipliedAlpha to false for those .png's in your Content Project.
Unfortunately, I don't know how to resolve this issue when using Texture2d.FromStream().

Segmenting Part of the image using threshold

Im trying to isolate and segment the yellow car body to change the color of it. in order to do that i need to separately identify the body from the image. And continue oration with the remaining white pixels. And im using C#, here the plan
Color d;
Color newColor = Color.YellowGreen;
for(inti =0;i<carimage.Width;i++){
for(intj =0;j<carimage.Height;j++){
d = carimage.GetPixel(i, j);
if(d.R == 255 && d.G==255 && d.B == 255)
image.SetPixel(i, j, newColor );
}
}
simple thresholding will trow the second image where car body is not separated correctly. i tried Aforge.net Fill holes image filter but no significant change has been done to the threshold image. I tried to use color filter but it i did not return a correct output due to color vary of the body. can anyone suggest and solution for this?
Original Image
Threshold Image
Instead of thresholding, you might want to look into clustering.
As a quick&dirty test, I've increased the image brightness in HSB space (using Mathematica):
brightnessAdjusted = Image[
Map[#^{1, 1, 0.2} &, ImageData[ColorConvert[img, "HSB"]], {2}],
ColorSpace -> "HSB"]
Then I've used simple K-Nearest clustering:
(clusters = ClusteringComponents[ColorConvert[brightnessAdjusted, "RGB"], 3,
Method -> "KMeans"]) // Colorize
to find clusters of similar colors in the image (there are many more, probably more suitable clustering algorithms, so you should experiment a little). Then I can just adjust the color in one of the clusters:
Image[MapThread[If[#1 == 2, #2[[{1, 3, 2}]], #2] &, {clusters, ImageData[brightnessAdjusted]}, 2]]
If you want to use thresholding, you should probably use a CIE color space, since euclidian distances in that color space are closer to human perception.
I had a similar project few years ago. I can't remember the exact details, but the idea was to shift a (not too small) sliding window over the image, and calculate the average intensity (maybe for R, G and B separately) inside the window at each position. I filled a "threshold image" with these averages, and subtracted it from the original image. There was a scaling factor somewhere, and other tuning stuff, but the point is, such an approach was way better than using a constant threshold.
If you are going to use a set of thresholds, you might be better of selecting yellow hues in the Hue Saturation Value colorspace. See the related SO question.
I=imread('test.jpg');
I=im2double(rgb2gray(I));
BW=im2bw(I,0.64);imshow(BW)
Gives me :
I got the 0.64 threshold by looking at the image's histogram. I suggest you use MATLAB to do image processing as it is much easier. Hope that helps you in colouring the image.

Categories