Search code examples
c#unsafe

Unsafe Image manipulation causing program exit


I am experimenting with some basic image manipulation using unsafe code. I am then accessing a pixel with bmp.GetPixel and this is causing the program to "stop working". I have no idea how to debug this.

Am I not doing something in the Treshold method that I should be doing?

var imageFilename = @"foo.jpg";
var im = (Bitmap)Bitmap.FromFile(imageFilename);

Threshold(im, 2);

// this line causes it to stop working without an exception
im.GetPixel(0,0);



static void Threshold(Bitmap bmp, int thresh)
{
    BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);

    unsafe
    {
    byte* p = (byte*)(void*)bmData.Scan0.ToPointer();
    int h = bmp.Height;
    int w = bmp.Width;
    int ws = bmData.Stride;

    for (int i = 0; i < h; i++)
    {
        byte* row = &p[i * ws];
        for (int j = 0; j < w * 3; j += 3)
        {
        row[j] = (byte)((row[j] > (byte)thresh) ? 255 : 0);
        row[j + 1] = (byte)((row[j + 1] > (byte)thresh) ? 255 : 0);
        row[j + 2] = (byte)((row[j + 2] > (byte)thresh) ? 255 : 0);
        }
    }
    }

    bmp.UnlockBits(bmData);
}

Update: For some reason, I discovered that using a different pixel format PixelFormat.Format24bppRgb solved the problem. Why? The input image is greyscale.


Solution

  • The problem is specifically image format. Grayscale images are 8 or 16 bits per pixel (depending on the image), not 24 bits per pixel; you're reading from (and more detrimentally, writing to) memory locations past the image - unsafe code does not check array bounds, so no exception is thrown.

    For example, for 16-bit grayscale, re-write the inner loop:

        for (int j = 0; j < w * 2; j += 2)
        {
           row[j] = (byte)((row[j] > (byte)thresh) ? 255 : 0);
           row[j + 1] = (byte)((row[j + 1] > (byte)thresh) ? 255 : 0);
        }
    

    The program "stops working" as you attempt to work with the image after the unsafe code runs, due to corrupt memory - you wrote to memory not owned by the image.

    You can also adjust your loop based on bmp.PixelFormat, and manipulate an appropriate amount of bits; this way your code will work for multiple image formats.

    More info on bits per pixel