views:

394

answers:

3

From what I have read on the internet the Y value is the luminance value and can be used to create a grey scale image. The following link: http://www.bobpowell.net/grayscale.htm, has some C# code on working out the luminance of a bitmap image :

{
  Bitmap bm = new Bitmap(source.Width,source.Height);
  for(int y=0;y<bm.Height;y++)        public Bitmap ConvertToGrayscale(Bitmap source)
  {
    for(int x=0;x<bm.Width;x++)
    {
      Color c=source.GetPixel(x,y);
      int luma = (int)(c.R*0.3 + c.G*0.59+ c.B*0.11);
      bm.SetPixel(x,y,Color.FromArgb(luma,luma,luma));
    }
  }
  return bm;
}

I have a method that returns the YUV values and have the Y data in a byte array. I have the current piece of code and it is failing on Marshal.Copy – attempted to read or write protected memory.

public Bitmap ConvertToGrayscale2(byte[] yuvData, int width, int height)
        {
            Bitmap bmp;
            IntPtr blue = IntPtr.Zero;

            int inputOffSet = 0;
            long[] pixels = new long[width * height];

            try
            {
                for (int y = 0; y < height; y++)
                {
                    int outputOffSet = y * width;
                    for (int x = 0; x < width; x++)
                    {
                        int grey = yuvData[inputOffSet + x] & 0xff;
                        unchecked
                        {
                            pixels[outputOffSet + x] = UINT_Constant | (grey * INT_Constant);
                        }
                    }

                    inputOffSet += width;
                }

                blue = Marshal.AllocCoTaskMem(pixels.Length);
                Marshal.Copy(pixels, 0, blue, pixels.Length); // fails here : Attempted to read or write protected memory


                bmp = new Bitmap(width, height, width, PixelFormat.Format24bppRgb, blue);

            }
            catch (Exception)
            {

                throw;
            }
            finally
            {
                if (blue != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(blue);
                    blue = IntPtr.Zero;
                }

            }

            return bmp;
        }

Any help would be appreciated?

+1  A: 

I think you have allocated pixels.Length bytes, but are copying pixels.Length longs, which is 8 times as much memory (a long is 64 bits or 8 bytes in size).

You could try:

blue = Marshal.AllocCoTaskMem(Marshal.SizeOf(pixels[0]) * pixels.Length); 

You might also need to use int[] for pixels and PixelFormat.Format32bppRgb in the Bitmap constructor (as they are both 32 bits). Using long[] gives you 64 bits per pixel which isn't what a 24 bit pixel format is expecting.

You might end up with shades of blue instead of grey though - depends on what your values of UINT_Constant and INT_Constant are.

There is no need to do "& 0xff", as yuvData[] already contains a byte.

Saxon Druce
I've change pixels to int[] and the changed pixelFormat as suggested. Now I'm geting: Cannot implicitly convert type 'long' to 'int'. I'm guessing I should change these to something else:private const uint UINT_Constant = 0xFF000000; private const int INT_Constant = 0x00010101;
What line has that error message?
Saxon Druce
pixels[outputOffSet + x] = UINT_Constant | (grey * INT_Constant);
Try this: pixels[outputOffSet + x] = (int)(UINT_Constant | (grey * INT_Constant)). Also I would suggest making INT_Constant a uint as well, to remove the bitwise-or warning.
Saxon Druce
I'm get StackOverflowException. The UINT_Constant is cast as 4278190080
If I change unchecked to checked then I get an OverflowException (not a StackOverflowException) on that line, but with your code as shown I don't get an exception on that line. Or are you getting a StackOverflowException somewhere else? Ideally pixels would be a uint[] not an int[], but there's no Marshal.Copy() override which takes a uint[]. Another alternative would be to make pixels an array of bytes instead.
Saxon Druce
+1  A: 

Here are another couple of approaches you could try.

public Bitmap ConvertToGrayScale(byte[] yData, int width, int height)
{
    // 3 * width bytes per scanline, rounded up to a multiple of 4 bytes
    int stride = 4 * (int)Math.Ceiling(3 * width / 4.0);

    byte[] pixels = new byte[stride * height];
    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            byte grey = yData[y * width + x];
            pixels[y * stride + 3 * x] = grey;
            pixels[y * stride + 3 * x + 1] = grey;
            pixels[y * stride + 3 * x + 2] = grey;
        }
    }

    IntPtr pixelsPtr = Marshal.AllocCoTaskMem(pixels.Length);
    try
    {
        Marshal.Copy(pixels, 0, pixelsPtr, pixels.Length);

        Bitmap bitmap = new Bitmap(
            width, 
            height, 
            stride, 
            PixelFormat.Format24bppRgb, 
            pixelsPtr);
        return bitmap;
    }
    finally
    {
        Marshal.FreeHGlobal(pixelsPtr);
    }
}

public Bitmap ConvertToGrayScale(byte[] yData, int width, int height)
{
    // 3 * width bytes per scanline, rounded up to a multiple of 4 bytes
    int stride = 4 * (int)Math.Ceiling(3 * width / 4.0);

    IntPtr pixelsPtr = Marshal.AllocCoTaskMem(stride * height);
    try
    {
        for (int y = 0; y < height; y++)
        {
            for (int x = 0; x < width; x++)
            {
                byte grey = yData[y * width + x];
                Marshal.WriteByte(pixelsPtr, y * stride + 3 * x, grey);
                Marshal.WriteByte(pixelsPtr, y * stride + 3 * x + 1, grey);
                Marshal.WriteByte(pixelsPtr, y * stride + 3 * x + 2, grey);
            }
        }

        Bitmap bitmap = new Bitmap(
            width,
            height,
            stride,
            PixelFormat.Format24bppRgb,
            pixelsPtr);
        return bitmap;
    }
    finally
    {
        Marshal.FreeHGlobal(pixelsPtr);
    }
}
Saxon Druce
Tried both examples. Getting 'AccessViolationException' after awhile, happens mostly when I resize form, on both examples. "Attempted to read or write protected memory. Image displayed is grey scale pyjama stripes.
On what line do you get the access violation?
Saxon Druce
A: 

I get a black image with a few pixel in the top left corner if I use this code and this is stable when running :

 public static Bitmap ToGrayscale(byte[] yData, int width, int height)
    {
        Bitmap bm = new Bitmap(width, height, PixelFormat.Format32bppRgb);
        Rectangle dimension = new Rectangle(0, 0, bm.Width, bm.Height);
        BitmapData picData = bm.LockBits(dimension, ImageLockMode.ReadWrite, bm.PixelFormat);
        IntPtr pixelStateAddress = picData.Scan0;

        int stride = 4 * (int)Math.Ceiling(3 * width / 4.0);
        byte[] pixels = new byte[stride * height];

        try
        {
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    byte grey = yData[y * width + x];
                    pixels[y * stride + 3 * x] = grey;
                    pixels[y * stride + 3 * x + 1] = grey;
                    pixels[y * stride + 3 * x + 2] = grey;

                }
            }

            Marshal.Copy(pixels, 0, pixelStateAddress, pixels.Length);
            bm.UnlockBits(picData);
        }
        catch (Exception)
        {
            throw;
        }

        return bm;
    }
When using LockBits() you should use picData.Stride instead of calculating it yourself. Note also that it can be negative (in which case Scan0 points to the last scanline). Also with the 3 * x multiplier, you should use Format24bppRb instead of 32.
Saxon Druce
Ok, turns out that I wasn't getting the correct Y data, so that has now been fixed and I'm now getting a greyscale image using the above code.Thanks for you help Saxon.