Trying to find a solution for how to recognize if there is a "green" color on particular screenshot (image below).
The problem is that when using a plain RGB Bitmap, it doesn't work as I would like to because the image is taken from a screenshot of a webpage and it's kind of blurry, so many pixels doesn't look like "green".
I need somehow to understand how to define whether there is "green" color on a particular screenshot
I need somehow to know whether there is green color
Iterate all pixels and search for the desired color
Color c = Color.Green; //or the color you want to search
Bitmap bmp = new Bitmap(Image.FromFile(#"c:\file.bmp"));
bool containsgreen = false;
for (int w = 0; w < bmp.Width; w++)
for (int h = 0; h < bmp.Height; h++)
if (bmp.GetPixel(w, h) == c)
containsgreen = true;
If you're looking for a color range or similar colors, you could also calculate the color distance to add tolerance
public static double GetColourDistance(Color e1, Color e2)
{
return Math.Sqrt((e1.R - e2.R) * (e1.R - e2.R) + (e1.G - e2.G) * (e1.G - e2.G) + (e1.B - e2.B) * (e1.B - e2.B));
}
I am going to assume from your question, that when you say green, you don't mean that any pixel will have some positive value for the G component of the RGB color, but that you mean it looks visually green to a human.
If that is the case, I suggest a modification to #fubo's code that calculates "visually green". That would be when the G component is greater than the other components.
Now, this will return true for some sketchy greens, e.g. a green that is very, very dark or very, very light. If you want to filter those out, use a tolerance value of your choosing.
Here's the code:
bool HasGreen(int tolerance)
{
using (var bmp = new Bitmap(Image.FromFile(#"c:\file.bmp")))
{
for (int w = 0; w < bmp.Width; w++)
for (int h = 0; w < bmp.Height; h++)
if (IsGreenPixel(bmp.GetPixel(w, h), tolerance))
return true;
}
return false;
}
bool IsGreenPixel(Color color, int tolerance)
=> color.G > color.R + tolerance && color.G > color.B + tolerance;
If you're looking for "what is the main green color in the green colors", you could modify this algorithm further by doing counts of colors and dropping them into buckets (i.e. a histogram).
Related
I'm using the following code on an image who has only black/white values so that if a color is black it should be counted but somehow the following if statement doesn't work. Is it written correctly or Im just using a good logic here
for (int y = 0; y < image.Height; y++)
{
Color pixel = image.GetPixel(x, y);
if(pixel.R > 0)
{
//some code here
}
}
Assuming no transparency, try
if(pixel == Color.Black)
....
(pixel.R>0 just checks color's Red component. It is 0 for Black.)
For barcodes, it might be better to use some thresholds to differentiate colors, e.g.:
int threshold = (255 + 255 + 255) / 2;
if (pixel.R + pixel.G + pixel.B < threshold)
....
I need help for my assignment. Basically, this is what I want to do:
Load an image to a PictureBox
Calculate the difference between 255 (the maximum value) and the R value in each pixel, and the difference between 255 and G value, and also for B value
From the calculation above, the least absolute value will indicate that the pixel's color is closer to that color (ex: (255-R value) has the smallest absolute value, so the pixel is closer to that color)
Change the pixel color to the closer color (in example above, it means change it to Red)
Display the result in the output picturebox
As the result, I will obtain the image with those three primary colors.
I have written the code like this:
Bitmap img = new Bitmap(InputPictureBox.Image);
byte R, G, B;
Color pixelColor;
for (int x = 0; x < img.Width; x++)
{
for (int y = 0; y < img.Height; y++)
{
pixelColor = img.GetPixel(x, y);
R = (byte) Math.Abs(pixelColor.R - 255);
G = (byte) Math.Abs(pixelColor.G - 255);
B = (byte) Math.Abs(pixelColor.B - 255);
if (R < G && R < B)
{
pixelColor = Color.Red;
}
else if (G < R && G < B)
{
pixelColor = Color.Green;
}
else if (B < R && B < G)
{
pixelColor = Color.Blue;
}
}
}
OutputPictureBox.Image = img;
The problem is that the color image then turn to be inverted. So, what is wrong in my code? I assume that the if statements don't work, but I don't know why. Am I wrong?
One more question related to my code above, can it actually work by simply calculating the gap of R/G/B value like that OR it absolutely has to be done by using euclidean distance?
If you don't mind please show me how to fix this or maybe how the code should be written. I ever read a quite similar question, but the given answer still didn't give me a clue.
Your code actually works, although there is a bit of overthinking put into it.
Try this:
The code has been moved to the Update section at the bottom of the post
Result:
I've removed the overthinking part.
There's no reason (at least from reading your question) why you need to invert the color component values;
Simply doing R = pixelColor.R is enough;
And through this you don't have to think of it as "which has the least amount of
red", but rather, "if it has the most amount of red, it's red!"
As LightStriker pointed out: You are missing (it is nowhere in your code) the code to set new value back into the image;
This is accomplished using img.SetPixel(x, y, pixelColor).
I've added an else clause to match pixels where no single color component is greater than both others.
For example, Yellow (255, 255, 0) would not be matched by your rules;
Using the version in this answer, it gets replaced by a Black pixel.
Update: per the comments below asking for additional clarification. Here's how you would add more conditional statements:
// NEW (start) --------------------------------------------------
Color[] randomizedColors = new Color[] { Color.Red, Color.Green, Color.Blue };
Random randomizer = new Random();
// NEW (end) --------------------------------------------------
Bitmap img = new Bitmap(InputPictureBox.Image);
byte R, G, B;
Color pixelColor;
// NEW (start) --------------------------------------------------
Func<int, Color> ColorRandomizer = (numberOfColors) =>
{
if (numberOfColors > randomizedColors.Length)
{
numberOfColors = randomizedColors.Length;
}
return randomizedColors[randomizer.Next(numberOfColors)];
};
// NEW (end) --------------------------------------------------
for (int x = 0; x < img.Width; x++)
{
for (int y = 0; y < img.Height; y++)
{
pixelColor = img.GetPixel(x, y);
R = pixelColor.R;
G = pixelColor.G;
B = pixelColor.B;
if (R > G && R > B)
{
pixelColor = Color.Red;
}
else if (G > R && G > B)
{
pixelColor = Color.Green;
}
else if (B > R && B > G)
{
pixelColor = Color.Blue;
}
// NEW (start) --------------------------------------------------
else if (pixelColor == Color.Yellow)
{
// 2 = Red or Green
pixelColor = ColorRandomizer(2);
}
else if (pixelColor = Color.FromArgb(152, 152, 152))
{
// 3 = Red, Green, or Blue
pixelColor = ColorRandomizer(3);
}
/* else if (pixelColor = Some_Other_Color)
{
// 3 = Red, Green, or Blue
pixelColor = ColorRandomizer(3);
} */
// NEW (end) --------------------------------------------------
else
{
pixelColor = Color.Black;
}
img.SetPixel(x, y, pixelColor);
}
}
OutputPictureBox.Image = img;
With this updated code, add all colors that should be picked randomly to the randomizedColors array. Use the lambda function, ColorRandomizer, to assist in choosing a color randomly; keep in mind that this function will randomly pick between the first element and the one specified.
Following is inverting all the colors.
R = (byte) Math.Abs(pixelColor.R - 255);
G = (byte) Math.Abs(pixelColor.G - 255);
B = (byte) Math.Abs(pixelColor.B - 255);
You can use:
R = (byte) pixelColor.R;
G = (byte) pixelColor.G;
B = (byte) pixelColor.B;
I have an Image and I can Read all it's Pixels colors using Martix ... How can I change The RGB for any Pixel If I want to convert it to the nearest color from (Black , Red or white)
My Code to Read the Image in Matrix is :
string sourceimg = #"D:\ProductionTools\Taskes\Image Processing\Test\001.jpg";
//...
Bitmap imageoriginal = new Bitmap(sourceimg);
int height = imageoriginal.Height;
int width = imageoriginal.Width;
Color[][] colormatrix = new Color[width][];
for (int i = 0; i < width; i++) {
colormatrix[i] = new Color[height];
for (int j = 0; j < height; j++) {
colormatrix[i][j] = new Color();
colormatrix[i][j] = imageoriginal.GetPixel(i, j);
}
}
As pointed out in your comments, the question is about color distance, which is not a trivial matter. It also relates to which colorspace you use, so here I will show you examples in RGB, HSV, HLS, and CIELAB. Also, to calculate distance you need a formula. For simplicity, let's sticky to the euclidean distance, as in
Then to trivially answer your question, you calculate the distance from the current color q to the three targets pi you have (black, red, and white). The smallest distance indicates the color you replace with. In case of tie, I keep the earliest color that gave the minimum distance.
Now, the colorspace is also very important in this task since it establishes the meaning for the euclidean distance. Here is a sample image:
Converted in RGB:
Converted in HLS (i.e., the RGB color is converted to HLS and the distance is calculated):
HSV:
CIELAB:
As has been pointed out, the hardest part of this problem is knowing whether a given color is "closer" to black, white or red. I've thrown something together that might work for you:
Color GetNearestOfBWR(Color c)
{
float redness = Math.Abs(180 - c.GetHue()) / 180;
float brightness = c.GetBrightness();
float saturation = c.GetSaturation();
You now have three values, each between 0 and 1, where increasing from 0 to 1 means you are getting closer to red (roughly speaking: more red/less green&blue, more color/less black, more color/less grey).
You now have to decide at what point a color constitutes "red" and honestly that's a judgment call. You could simply have
double brightColourfulRedness = Math.Sqrt(
redness*redness + brightness*brightness + saturation*saturation);
if (brightColourfulRedness > 1)
return Color.FromArgb(255, 0, 0); // red;
(simple Euclidean norm) but you might want to weight a particular property more strongly than another - or simply modify the 1 threshold.
Then you have to decide what maps to black or to white; this could be as simple as
if (brightness > 0.5)
return Color.FromArgb(255, 255, 255); // white
return Color.FromArgb(0, 0, 0); // black
}
Great! So now you have a function to map a color to red, white or black, according to taste. All that remains is to apply it to each pixel in your bitmap. If you're happy overwriting the Bitmap you loaded from your file, you don't need the array; you can just do:
int height = imageoriginal.Height;
int width = imageoriginal.Width;
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
Color origC = imageoriginal.GetPixel(i, j);
Color newC = GetNearestOfBWR(origC);
imageoriginal.SetPixel(i, j, newC);
}
}
This can be quite slow, so you could alternatively use DmitryG's LockBits code, but you'd need to convert between the ints it gives you and the Colors required by GetNearestOfBWR. (FromArgb and ToArgb do this, I believe.)
I am developing a taskbar for the 2nd screen(something like displayfusion).
However, I'm having difficulty at getting the right average color from the icon. For example Google Chrome/ When I hover it on the main taskbar it backgrounds turns yellow. With my code it turns orange/red.
This is what it looks now:
How can I get the right dominant/average color?
I use this code to calculate the average color:
public static Color getDominantColor(Bitmap bmp)
{
//Used for tally
int r = 0;
int g = 0;
int b = 0;
int total = 0;
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
Color clr = bmp.GetPixel(x, y);
r += clr.R;
g += clr.G;
b += clr.B;
total++;
}
}
//Calculate average
r /= total;
g /= total;
b /= total;
return Color.FromArgb(r, g, b);
}
The average color is not neccessarily the color most used. I recommend calculating the HUE of pixels which have saturation over a certain threshold, and use an array to create a histogram of the image. (How many times a certain hue value was used).
Then smooth the histogram (calculate local average values with both neighbours), then get the place where this smoothed histogram takes the maximal value.
You can get HSL values with:
Color.GetHue
Color.GetSaturation
Color.GetBrightness
I am trying to generate the below Color Gradient ( the Color is blue at one end, and red at the other).
(source: brothersoft.com)
I follow the suggestion put forth here. This is my code:
int rMax = Color.Red.R;
int rMin = Color.Blue.R;
// ... and for B, G
var colorList = new List<Color>();
for(int i=0; i<size; i++)
{
var rAverage = rMin + (int)((rMax - rMin) * i / size);
// ... and for B, G
colorList.Add(Color.FromArgb(rAverage, gAverage, bAverage));
}
Although the result I did show a gradual, smooth transition from Red to Blue, but other intermediate color such as yellow and green didn't show up at all.
Anything I did wrong?
You should work with colors in the HSL color space, not RGB. That allows you to smoothly change the hue value from 0 (red) to 1 (violet). The System.Drawing.Color structure allows converting from RBG to HSL (GetHue etc) but not the other way. The math is simple though.
int rMax = Color.Red.R;
int rMin = Color.Blue.R;
// ... and for B, G
var colorList = new List<Color>();
for(int i=0; i<size; i++)
{
var rAverage = rMin + (int)((rMax - rMin) * i / size);
// ... and for B, G
colorList.Add(Color.FromArgb(rAverage, gAverage, bAverage));
}
You are setting rMin = 0 and rMax = 255. Thus you are essentially setting
rAverage = 255 * i / size;
You don't have the code for gAverage and bAverage listed, but if it were truly equivalent you'd be seeing a gradual trasition from black->gray->white, with no other hues at all.
It seems likely that what you want is to iterate over the different hues at a constant lightness/saturation. See here for an example C# class which does that, and here for an explanation of the equation.