1

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

AARRGGHHH
  • 127
  • 4
  • 9
  • You should try to post one of your attempts so we can help you better. For speed you should eventually use `LockBits` but for now and to learn about basics a regular loop over the pixels in the `Bitmap` using `SetPixel` will do. After that you need to __re-assign__ the changed Bitmap to the `PictureBox.Image` ! – TaW Mar 01 '15 at 10:56
  • One quick question: how do I re-assign the changed bitmap? Failing to do so is probably why I'm not seeing any results. I'm refreshing the pictureBox, but that's not getting it done. Thank you – AARRGGHHH Mar 01 '15 at 21:24
  • After you are done with the changes to a `Bitmap bmp` you simply write `picturebox1.Image = bmp;` – TaW Mar 01 '15 at 21:30
  • PS: I'm trying pictureBox1.Image = null; pictureBox1.Image = bmp; pictureBox1.Refresh(); but not seeing results. Removing null also doesn't work. Thanks – AARRGGHHH Mar 01 '15 at 21:41
  • Getting some results now, I need to fine tune this code later, and then post it so everyone can help more, as you suggested. Thanks you – AARRGGHHH Mar 01 '15 at 21:48
  • I have added an example code. BTW: I think you have never __accepted__ an answer. You should accept answers that solved or helped solve your problems instead of leaving the questions open! – TaW Mar 01 '15 at 22:01
  • Sorry, I just saw your sample code now, it was hidden behind something like "1 more comment". I'll check it out now, thank you very much! – AARRGGHHH Mar 02 '15 at 02:57
  • `ModifyHue`is not a .NET function but one somebody has written in his code. You can see in my answer how you can construct a new Color from the A,R,G and B values of an old Color. The advantage is that it will keep the loop small and the code better organized.. If you want I can expande my answer to show you the combination of Lockbits and a color filter function.. – TaW Mar 02 '15 at 07:53
  • Your sample code is working very well, thank you. I'm adding to it so the user can make minor changes to the color of the image and save the changes. If you don't mind expanding the discussion to cover Lockbits and color filter, I'm sure it would be helpful, **thank you very much**. – AARRGGHHH Mar 02 '15 at 08:50

1 Answers1

3

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;
}

enter image description hereenter image description here

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

Community
  • 1
  • 1
TaW
  • 53,122
  • 8
  • 69
  • 111
  • This has definitely set me in the right direction, additional thoughts are welcomed. Thank you! – AARRGGHHH Mar 02 '15 at 03:12
  • I have exoanded the answer to include and more complex solution. It is very fast and rather flexivle. The is quite a lot to study there but don't worry, you don't need to understand every bit of it to use and modify it.. – TaW Mar 02 '15 at 10:38