0

In the code below I want to convert an image to a byte array, modify the value of some pixels, and convert it back to an image. Everything works fine if I don't modify any pixel value. The two conversions work. But if I try to modify a pixel value between the two conversions I get an exception. Can you tell me what I am doing wrong ?

        OpenFileDialog openFileDialog = new OpenFileDialog();
        openFileDialog.ShowDialog();
        FileStream bmpstream = new FileStream(openFileDialog.FileName, FileMode.Open, FileAccess.Read);
        Bitmap spotImage = new Bitmap(bmpstream);

        ImageConverter imgCon = new ImageConverter();
        byte[] rgbValues = (byte[])imgCon.ConvertTo(spotImage, typeof(byte[]));

        for (int counter = 2; counter < rgbValues.Length; counter += 3)
        {    

            Buffer.SetByte(rgbValues, counter, 0x00);
            //rgbValues[counter] = 0x00; <--I tried also this (not sure of the difference) and I get the same result

        }

        Image image2 = null;
        using (MemoryStream mStream = new MemoryStream(rgbValues, 0, rgbValues.Length))
        {

                image2 = Image.FromStream(mStream, true); //this line gives the exception

        }
        pictureBox2.Image = image2;

I also tried to open the file using Bitmap.LockBits and got the same result. Here is the second version of the code (again, it worked as long as I didn't modify the byte array):

        Rectangle rect = new Rectangle(0, 0, spotImage.Width, spotImage.Height);
        BitmapData bmpData = spotImage.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, spotImage.PixelFormat);
        IntPtr pointeur = bmpData.Scan0;
        int bytes = Math.Abs(bmpData.Stride) * bmpData.Height;
        byte[] rgbValues = new byte[bytes];
        System.Runtime.InteropServices.Marshal.Copy(pointeur, rgbValues, 0, bytes);
        for (int counter = 2; counter < rgbValues.Length; counter += 3)
        {

            Buffer.SetByte(rgbValues, counter, 0x00);
            //rgbValues[counter] = 0x00;

        }
        spotImage.UnlockBits(bmpData);


        Image image2 = null;
        using (MemoryStream mStream = new MemoryStream(rgbValues, 0, rgbValues.Length))
        {

                image2 = Image.FromStream(mStream, true); //this line gives the exception

        }
        pictureBox2.Image = image2;
6
  • Please add the exception message and stacktrace. And welcome to SO. Commented Mar 25, 2020 at 15:46
  • What kind of image data are we talking about here? Are you sure you can simply modify pixels by changing raw bytes? (i.e. changing the wrong bytes might corrupt an image file, depending on the format) Commented Mar 25, 2020 at 15:49
  • Thank you for your help. The image is a bitmap, I edited the code to add this information. And the exception says : System.ArgumentException : 'Parameter is not valid.' Commented Mar 25, 2020 at 19:08
  • 2
    The code messes up the header that is in front of the pixel data. The specific file format matters a lot as well. Avoid all this by using Bitmap.LockBits() instead, it guarantees direct access to the pixels. Commented Mar 25, 2020 at 19:34
  • I also tried with LockBits and got the same exception. I don't understand very well the piece of code that I had to use for that, but it did work as long as I didn't modify anything in the image. I will update my question to add this other version. Commented Mar 25, 2020 at 19:51

1 Answer 1

1

Your both attempts failed, because as in the comments:

  1. First attempt - you mess up bytes in file header
  2. Second attempt - you try to decode raw pixel data as Image

Your second attempt is much closer to correct solution. Lock bits with Write enabled and stay with the image you already open - no need to rebuild new bitmap (if there is actually such need in your specific case then I leave this to you as an exercise):

OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.ShowDialog();
FileStream bmpstream = new FileStream(openFileDialog.FileName, FileMode.Open, FileAccess.Read);
Bitmap spotImage = new Bitmap(bmpstream);

Rectangle rect = new Rectangle(0, 0, spotImage.Width, spotImage.Height);
BitmapData bmpData = spotImage.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, spotImage.PixelFormat);
IntPtr pointeur = bmpData.Scan0;
int bytes = Math.Abs(bmpData.Stride) * bmpData.Height;
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(pointeur, rgbValues, 0, bytes);

var pixelSize = Image.GetPixelFormatSize(bmpData.PixelFormat);

for (int counter = 2; counter < rgbValues.Length; counter += 3)
{
    rgbValues[counter] = 0x00;
}
// copy buffer back to image
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, pointeur, bytes);

spotImage.UnlockBits(bmpData);

pictureBox2.Image = spotImage;

This should work. I don't know what effect on the image you wanted to achieve though.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much, this works indeed! What I am doing with the image is still pointless now, I am just trying to start simple before doing what I want.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.