I am trying to get the Hue or 'H' of every pixel of an image, then changing the colour of the pixel that has a specific Hue. I am trying to change all pixels that have a hue >= 210 and <=260, which is the different hues of blue.
Here is my code:
// 'i' is the image
// 'b' is the bitmap of the image
float y;
for (int a = 0; a < i.Height; a++)
{
for (int c = 0; c < i.Width; c++)
{
y = b.GetPixel(c, a).GetHue();
if (y >= 210 && y <= 260)
{
b.SetPixel(c, a, Color.Black);
}
}
}
The only problem is that it doesn't pickup any blue colours, which are from 210 - 260. I am pretty sure that I am doing this correctly, but I must not be since it isnt working.
Please post here if you can solve this problem, Thanks!
Edit: I put a breakpoint on SetPixel, and it gets called many, many times so now I will check if I am saving the picture right.
Edit 2: I figured it out! I wasn't saving the picture.
It kind of worked for me. In the code below I have a Windows Form with PictureBox called imgViwer. Then clicking the a button I execute the code:
private void HueFilter()
{
float y;
Bitmap i = (Bitmap)imgViwer.Image;
for (int a = 0; a < i.Height; a++)
{
for (int c = 0; c < i.Width; c++)
{
y = i.GetPixel(c, a).GetHue();
if (y >= 210 && y <= 260)
{
i.SetPixel(c, a, Color.Black);
}
}
}
imgViwer.Image = i;
}
In your case you have an output image called b that should be assigned back to the PictureBox to the refresh.
Use, LockBits method, Luke!
Or, i think, it`s better be done with Graphics context.
Related
I receive images of the same size but with different amounts of information. Examples below (red borders are mine). The background is always white.
I am trying to detect where the information on the image ends - at what pixel height (and crop accordingly). In other words, find the first non-white pixel from the bottom.
Is there a better way to do this other than extract BitmapData out of Image object and loop through all the pixels?
Just to add a suggestion having looked over your images and your solution (below) and your method is fine but you may be able to improve efficiency.
The more you know about your image the better; you're confident the background is always white (according to your post, the code is a more generic utility but the following suggestion can still work); can you be confident on the furthest point in a non-white pixel will be found if the row is not empty?
For example; in your two pictures the furthest in non-white pixel on a row is about 60px in. If this is universally true for your data then you don't need to scan the whole line of the image, which would make your for loop:
for (int y = bitmap.Height - 1; y >= 0; y--) {
for (int x = 0; x < 60; x++) {
Color color = bitmap.GetPixel(x, y);
if (color.R != backColor.R || color.G != backColor.G || color.B != backColor.B) {
foundContentOnRow = y;
break;
}
}
}
(You could make it a parameter on the function so you can easily control it if needed).
Imagine for example that the first non-white row was 80px down. To find it currently you do 640 x 300 = 192,000 checks. If you could confidently say that you would know a row was blank within 100 pixels (an over-estimate based on the data presented) then this would be 100 * 300 = 30,000 checks per image.
If you always knew that the first 10 pixels of the image were always blank you could shave a little bit more off (say 3000 checks).
Musing on a setup where you knew that the first non-white pixel was between 10 and 60 pixels in (range of 50) you could find it at row 80 in 50 x 300 = 15,000 checks which is a good reduction.
Of course the downside about assumptions is that if things change your assumptions may not be valid, but if the data is going to remain fairly constant then it may be worthwhile, especially if you do this for a lot of images.
I've ended up using the following code to trim the image. Hopefully someone finds this useful.
class Program {
static void Main(string[] args) {
Image full = Image.FromFile("foo.png");
Image cropped = full.TrimOnBottom();
}
}
public static class ImageUtilities {
public static Image TrimOnBottom(this Image image, Color? backgroundColor = null, int margin = 30) {
var bitmap = (Bitmap)image;
int foundContentOnRow = -1;
// handle empty optional parameter
var backColor = backgroundColor ?? Color.White;
// scan the image from the bottom up, left to right
for (int y = bitmap.Height - 1; y >= 0; y--) {
for (int x = 0; x < bitmap.Width; x++) {
Color color = bitmap.GetPixel(x, y);
if (color.R != backColor.R || color.G != backColor.G || color.B != backColor.B) {
foundContentOnRow = y;
break;
}
}
// exit loop if content found
if (foundContentOnRow > -1) {
break;
}
}
if (foundContentOnRow > -1) {
int proposedHeight = foundContentOnRow + margin;
// only trim if proposed height smaller than existing image
if (proposedHeight < bitmap.Height) {
return CropImage(image, bitmap.Width, proposedHeight);
}
}
return image;
}
private static Image CropImage(Image image, int width, int height) {
Rectangle cropArea = new Rectangle(0, 0, width, height);
Bitmap bitmap = new Bitmap(image);
return bitmap.Clone(cropArea, bitmap.PixelFormat);
}
}
I have an application that creates an image from a text inputed by the user. In that image, I want to detect where is the beginning of the letters in the top so I can stamp my brand's logo and crop image accordingly.
This is what I tried so far, but can't get it to work...
Bitmap myBitmap = new Bitmap(Image.FromFile(#"C:\Users\me\Desktop\test_textimage.jpg"));
for (int j = 0; j < 2000; j++) // loops Y axis
{
for (int i = 0; i < 3500; i++) // loops X axis
{
System.Drawing.Color color = myBitmap.GetPixel(i, j);
if (color == System.Drawing.Color.Black)
{ // breakpoint here... it never hits...
}
}
}
I am 100% sure the image is black font into white background. Tested into photoshop.
Can anyone help? Thanks.
I am using this code:
internal static Image ColorReplacer(Image Img, Color oldcolor, Color newcolor, int tolerence)
{
// Gotten From -> Code Project
Bitmap bmap = (Bitmap)Img.Clone();
Color c;
int iR_Min, iR_Max; int iG_Min, iG_Max; int iB_Min, iB_Max;
//Defining Tolerance
//R
iR_Min = Math.Max((int)oldcolor.R - tolerence, 0);
iR_Max = Math.Min((int)oldcolor.R + tolerence, 255);
//G
iG_Min = Math.Max((int)oldcolor.G - tolerence, 0);
iG_Max = Math.Min((int)oldcolor.G + tolerence, 255);
//B
iB_Min = Math.Max((int)oldcolor.B - tolerence, 0);
iB_Max = Math.Min((int)oldcolor.B + tolerence, 255);
for (int x = 0; x < bmap.Width; x++)
{
for (int y = 0; y < bmap.Height; y++)
{
c = bmap.GetPixel(x, y);
//Determinig Color Match
if ((c.R >= iR_Min && c.R <= iR_Max) &&
(c.G >= iG_Min && c.G <= iG_Max) &&
(c.B >= iB_Min && c.B <= iB_Max)
)
if (newcolor == Color.Transparent)
bmap.SetPixel(x, y, Color.FromArgb(0, newcolor.R, newcolor.G, newcolor.B));
else
bmap.SetPixel(x, y, Color.FromArgb(c.A, newcolor.R, newcolor.G, newcolor.B));
}
}
return (Image)bmap.Clone();
}
This code works very well. It changes my white icon image to another color successfully.
The problem is: Once I change it, I cannot change it again. It gives me the "Bitmap Region is already locked exception". I am assuming it's because GetPixel() is locking the image?
can anybody suggest a good solution to this problem ?
PS: I understand that GetPixel() is a very slow method, however, I am using 8 image and they are all 24px. They are very small so I don't think GetPixel()'s performance is that big of a problem.
I just tested your code, and it executed correctly (i.e. I was able to call ColorReplacer multiple times on a single image with different colors each time, and then paint it to a form).
Can you provide a sample of the code you are using that calls your ColorReplacer method?
you need to change the color as per the following process by locking and unlocking the image data:
http://msdn.microsoft.com/en-us/library/system.drawing.bitmap.unlockbits.aspx
So i'm trying to make a program that can find how many pixels of a specific colour it has in it. The images are pictures taken with camera and after that some areas on them has been marked on photoshop, and i need to find the exact number of this pixels. But i have few problems.
I'm using getPixel(x,y) but and i'm comparing to the Color.FromArgb(red, green, blue) that i want but... my first problem is that for colors differ a little for example i want to find out the color
RGB 116,110,40 but when you draw with this color on photoshop some pixels get a little diferent color like RGB 115,108,38 (and others similar) and i want to include this as well. So i finally came up with this code(but it seems that id does now work right):
public Form1()
{
InitializeComponent();
}
Bitmap image1;
int count=0;
int red, green, blue;
int redt, greent, bluet;
double reshenie;
private void button1_Click(object sender, EventArgs e)
{
try
{
red = int.Parse(textBox1.Text);
green = int.Parse(textBox2.Text);
blue = int.Parse(textBox3.Text);
// Retrieve the image.
image1 = new Bitmap(#"C:\bg-img.jpg", true);
double widht, height, pixel ;
int x, y;
MessageBox.Show(pixel.ToString());
// Loop through the images pixels
for (x = 0; x < image1.Width; x++)
{
for (y = 0; y < image1.Height; y++)
{
Color pixelColor = image1.GetPixel(x, y);
redt = pixelColor.R;
greent = pixelColor.G;
bluet = pixelColor.B;
if ((red+10>=redt) && (red-10>=redt))//i used +-10 in attempt to resolve the problem that i have writed about the close colours
{
if ((green + 10 >= greent) && (green - 10 >= greent))
{
if ((blue + 10 >= bluet) && (blue - 10 >= bluet))
{
count += 1;
}
}
}
}
}
pictureBox1.Image = image1;
MessageBox.Show("Imashe " + count.ToString());
count = 0;
}
catch (ArgumentException)
{
MessageBox.Show("There was an error." +
"Check the path to the image file.");
}
}
The problem is that i dont get the result that i expect. For example when i have to get like 1000 pixels i get more or less and i cant find where is my mistake. So if someone can give me idea what i'm doing wrong. Thanks for all the help in advance.
Try with this loop instead :
int epsilon = 10;
for (x = 0; x < image1.Width; ++x)
{
for (y = 0; y < image1.Height; ++y)
{
Color pixelColor = image1.GetPixel(x, y);
redt = pixelColor.R;
greent = pixelColor.G;
bluet = pixelColor.B;
if (Math.Abs(redt - red) <= epsilon &&
Math.Abs(greent - green) <= epsilon &&
Math.Abs(bluet - blue) <= epsilon)
{
++ count;
}
}
}
Where epsilon is the maximal difference between pixel color and targetted color, for each channel.
From your code:
if ((green + 10 >= greent) && (green - 10 >= greent))
If (a - 10 >= b), then for sure (a + 10 >= b). See if you can understand why.
I think you may have meant
if ((green - 10 <= greent) && (greent <= green + 10))
Ordering the condition like that helps with readability, because greent has to be between green - 10 and green + 10, and is also physically located between those expressions.
I think your color comparison is not right. You mixed the <= and >= in your attempt to be in a range around the color. Try this:
if ((red+10 >= redt) && (red-10 <= redt)) //i used +-10 in attempt to resolve the problem that i have writed about the close colours
{
if ((green + 10 >= greent) && (green - 10 <= greent))
{
if ((blue + 10 >= bluet) && (blue - 10 <= bluet))
{
count += 1;
}
}
}
I'm trying to remove all white or transparent pixels from an image, leaving the actual image (cropped). I've tried a few solutions, but none seem to work. Any suggestions or am I going to spend the night writing image cropping code?
So, what you want to do is find the top, left most non white/transparent pixel and the bottom, right most non white/transparent pixel. These two coordinates will give you a rectangle that you can then extract.
// Load the bitmap
Bitmap originalBitmap = Bitmap.FromFile("d:\\temp\\test.bmp") as Bitmap;
// Find the min/max non-white/transparent pixels
Point min = new Point(int.MaxValue, int.MaxValue);
Point max = new Point(int.MinValue, int.MinValue);
for (int x = 0; x < originalBitmap.Width; ++x)
{
for (int y = 0; y < originalBitmap.Height; ++y)
{
Color pixelColor = originalBitmap.GetPixel(x, y);
if (!(pixelColor.R == 255 && pixelColor.G == 255 && pixelColor.B == 255)
|| pixelColor.A < 255)
{
if (x < min.X) min.X = x;
if (y < min.Y) min.Y = y;
if (x > max.X) max.X = x;
if (y > max.Y) max.Y = y;
}
}
}
// Create a new bitmap from the crop rectangle
Rectangle cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X, max.Y - min.Y);
Bitmap newBitmap = new Bitmap(cropRectangle.Width, cropRectangle.Height);
using (Graphics g = Graphics.FromImage(newBitmap))
{
g.DrawImage(originalBitmap, 0, 0, cropRectangle, GraphicsUnit.Pixel);
}
public Bitmap CropBitmap(Bitmap original)
{
// determine new left
int newLeft = -1;
for (int x = 0; x < original.Width; x++)
{
for (int y = 0; y < original.Height; y++)
{
Color color = original.GetPixel(x, y);
if ((color.R != 255) || (color.G != 255) || (color.B != 255) ||
(color.A != 0))
{
// this pixel is either not white or not fully transparent
newLeft = x;
break;
}
}
if (newLeft != -1)
{
break;
}
// repeat logic for new right, top and bottom
}
Bitmap ret = new Bitmap(newRight - newLeft, newTop - newBottom);
using (Graphics g = Graphics.FromImage(ret)
{
// copy from the original onto the new, using the new coordinates as
// source coordinates for the original
g.DrawImage(...);
}
return ret
}
Note that this function will be slow as dirt. GetPixel() is unbelievably slow, and accessing the Width and Height properties of a Bitmap inside a loop is also slow. LockBits would be the proper way to do this - there are tons of examples here on StackOverflow.
Per-pixel check should do the trick. Scan each line to find empty line from the top & bottom, scan each row to find left & right constraints (this can be done in one pass with either rows or columns). When the constraint is found - copy the part of the image to another buffer.
In WPF we have a WriteableBitmap class. Is this what are you looking for ? If it is the case please have a look at http://blogs.msdn.com/b/jgalasyn/archive/2008/04/17/using-writeablebitmap-to-display-a-procedural-texture.aspx
I found a method to batch trim a few thousand .jpg files in about 10 minutes, but I didn't do it in code. I used the Convert feature of Snag-It Editor. I don't know if this is an option for you, if you need to do this trimming once or your need is ongoing, but for the price of the software, which isn't a whole lot, I considered this a decent workaround.
(I do not work for or represent Techsmith.)
Joey
Adding to this, if you are in WPF and you have excess space around your image, check the properties of the image and make sure your Stretch property is set to fill. This eliminated the space around the image.
Screen shot of the property in WPF