Adding or subtracting color from an image in a pictureBox using C#
I'd like to learn how to code some very basic image editing in Visual Studio. I'm using the openFileDialog to load a picture into a pictureBox. I've found some loops on the internet that are designed to convert color on a pixel by pixel basis, but for various reasons (that differ with each code sample) none work. What is the correct way to add to (or subtract from), for example, the red value, to change the tint of an image in a pictureBox? I'm using C#.
Thank you
EDIT: Here is an example of something that's at least a starting point:
Bitmap bmp = (Bitmap)Bitmap.FromFile(pictureBox1.ImageLocation);
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
bmp.GetPixel(x, y);
bmp.SetPixel(x, y, Color.FromArgb(128, 0, 128));
}
}
pictureBox1.Image = bmp;
MessageBox.Show("Done");
This allows me to get the image pixel by pixel, and change the color, in this case, to purple. Of course, that's not what I want to do. What I want to do, is get the original RGB value of each pixel, and increase or decrease the values. In other words, perform some very basic color correction.
How do I get the current RGB of each pixel, and set the RGB of the new pixel?
I have also seen this posted as an example. The problem is, I don't see how to use ModifyHue:
var bmp = new Bitmap(pictureBox1.ImageLocation);
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
Color oldColor = bmp.GetPixel(x, y);
Color newColor = ModifyHue(oldColor);
bmp.SetPixel(x, y, newColor);
}
}
pictureBox1.Image = bmp;
I realize I should have posted code samples the first time around. Thank you
This is an example of using Getpixel
and SetPixel
.
For (much much) faster filter results look into Lockbits and also into using a ColorMatrix
private void button2_Click(object sender, EventArgs e)
{
// we pull the bitmap from the image
Bitmap bmp = (Bitmap) pictureBox1.Image;
// we change some picels
for (int y = 100; y < bmp.Height; y++)
for (int x = 100; x < bmp.Width; x++)
{
Color c = bmp.GetPixel(x, y);
bmp.SetPixel(x, y, Color.FromArgb(255, 255, c.G, c.B));
}
// we need to re-assign the changed bitmap
pictureBox1.Image = (Bitmap) bmp;
}
Update:
The code above is a very simple introduction. It is simple but also very slow and it is not very flexible.
Here is a version that is both very fast and and much more flexible:
private void button3_Click(object sender, EventArgs e)
{
// pick one of our filter methods
ModifyHue hueChanger = new ModifyHue(MaxChannel);
// we pull the bitmap from the image
Bitmap bmp = (Bitmap)pictureBox1.Image;
Size s = bmp.Size;
PixelFormat fmt = bmp.PixelFormat;
// we need the bit depth and we assume either 32bppArgb or 24bppRgb !
byte bpp = (byte)(fmt == PixelFormat.Format32bppArgb ? 4 : 3);
// lock the bits and prepare the loop
Rectangle rect = new Rectangle(Point.Empty, s);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, fmt);
int size1 = bmpData.Stride * bmpData.Height;
byte[] data = new byte[size1];
System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, data, 0, size1);
// loops
for (int y = 0; y < s.Height; y++)
{
for (int x = 0; x < s.Width; x++)
{
// calculate the index
int index = y * bmpData.Stride + x * bpp;
// get the color
Color c = Color.FromArgb( bpp == 4 ?data[index + 3]: 255 ,
data[index + 2], data[index + 1], data[index]);
// process it
c = hueChanger(c, 2);
// set the channels from the new color
data[index + 0] = c.B;
data[index + 1] = c.G;
data[index + 2] = c.R;
if (bpp == 4) data[index + 3] = c.A;
}
}
System.Runtime.InteropServices.Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
bmp.UnlockBits(bmpData);
// we need to re-assign the changed bitmap
pictureBox1.Image = (Bitmap)bmp;
}
The above code calls a delegate
:
public delegate Color ModifyHue(Color c, int ch);
And the delegate
is set to call a simple filter function:
public Color MaxChannel(Color c, int channel)
{
if (channel == 1) return Color.FromArgb(255, 255, c.G, c.B);
if (channel == 2) return Color.FromArgb(255, c.R, 255, c.B);
if (channel == 3) return Color.FromArgb(255, c.R, c.G, 255);
else return c;
}
And here is another one that changes a Color
to grey
public Color ToGreyscale(Color c, int dummy)
{
byte val = (byte) ( (c.R * 0.299f + c.G * 0.587f + c.B *0.114f) ) ;
return Color.FromArgb(255, val, val,val);
}
Note that all methods we want to call via a delegate
need to have the same signature
. Therefore ToGreyscale
also takes an integer
as second parameter, even though it doesn't use it..
Also note that you can limit the LockBits
loop start and end values just like in the simple example before to get the second screenshot..