locked
Writing a 16-bit grayscale image RRS feed

  • Question

  • I have a byte array containing pixel values for a 16bpp grayscale image.  I need a solution for converting said array to a bitmap image.  So far I've come up with this:

     

    internal static Image imageFromArray(byte[] array)

    {

    const int WIDTH = 1472;

    int height = (array.Length) / WIDTH / 2;

    Bitmap bitmap = new Bitmap(1472, height, PixelFormat.Format16bppGrayScale);

    for (int x = 0; x < height; x++)

    for (int y = 0; y < WIDTH; y++)

    bitmap.SetPixel(x, y, (Color)array[(y * 1472 + x) * 2]); Image image = bitmap;

    return image;

     }

    But there is an error because I can't cast the byte as a color.  So my question is, how do I actually translate my pixel data from the byte[] to the bitmap?  Is there a better way to do this using an unsafe code block?

    Thanks for your help.  If its relevant, the image files I am using are about 16MB.  They contain raw data, some number of rows by 1472 columns of unsigned 16-bit words.  They are all 1472 pixels wide.

    Thursday, January 18, 2007 9:29 PM

Answers

  • Hi,

      what about if you use something like 48 bits per pixel RGB image, that way you will not lose any of your grayscale information when it is in the R, G or B component, then you will have to copy the data into each channel to make it gray and you should see you original picture, granted it will be  lot larger in memory, but will still be presented correctly.

    Saturday, January 20, 2007 1:33 AM

All replies

  • Hi,

      your code will probably be very slow due to calling SetPixel which has terrible performance.  You can do what you want to do by using the LockBits method,  the following has an example which should show you how to proceed: http://msdn2.microsoft.com/en-us/library/5ey6h79d.aspx

     

    Mark.

    Friday, January 19, 2007 12:45 AM
  • I read your link but I am not sure how to translate that to what I want to do.

    I have an array of bytes.  Every 2 bytes represents a grayscale value for a pixel in my image (16bpp).  I want to create a bitmap from these pixel values.

    Friday, January 19, 2007 1:58 PM
  • Hi,

      here is an example of how you would use the LockBits method to set bitmap image data:

     

    using System;
    using System.Drawing;
    using System.Drawing.Imaging;

    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                //Create pixel data to put in image, use 2 since it is 16bpp
                Random r = new Random();
                int width = 800;
                int height = 600;
                byte[] pixelValues = new byte[width * height * 2];
                for (int i = 0; i < pixelValues.Length; ++i)
                {
                    //Just create random pixel values, don't care about
                    //being greyscale values for example
                    pixelValuesIdea = (byte)r.Next(0, 256);
                }

                CreateBitmapFromBytes(pixelValues, width, height);
            }

            private static void CreateBitmapFromBytes(byte[] pixelValues, int width, int height)
            {
                //Create an image that will hold the image data
                Bitmap pic = new Bitmap(width, height, PixelFormat.Format16bppGrayScale);

                //Get a reference to the images pixel data
                Rectangle dimension = new Rectangle(0, 0, pic.Width, pic.Height);
                BitmapData picData = pic.LockBits(dimension, ImageLockMode.ReadWrite, pic.PixelFormat);
                IntPtr pixelStartAddress = picData.Scan0;

                //Copy the pixel data into the bitmap structure
                System.Runtime.InteropServices.Marshal.Copy(pixelValues, 0, pixelStartAddress, pixelValues.Length);

                pic.UnlockBits(picData);
                pic.Save(@"c:\users\mark\mypic1.bmp", ImageFormat.Bmp);
            }
        }
    }

     

    Mark.

    Friday, January 19, 2007 4:37 PM
  • This post was very helpful; however, the code will not work.

    I've researched and discovered that the PixelFormat Format16bppGrayScale is not supported.  Every time I try to run this code, my program crashes.

    :-(

    Converting to 8-bit is not an option.  I need all 65+K shades of gray.

    Friday, January 19, 2007 6:49 PM
  • Hi,

      I could run this code and create a bitmap without any issues.  What exception are you getting?  Is your byte array just pixel data or also image headers as well?

     

    Mark.

    Friday, January 19, 2007 7:01 PM
  • Here is my code:

    internal static Image imageFromArray(byte[] array)

    {

    int width = 1472;

    int height = array.Length / width / 2;

    Bitmap b = new Bitmap(width, height, PixelFormat.Format16bppGrayScale);

    BitmapData bmData = b.LockBits(new Rectangle(0, 0, width, height),

    ImageLockMode.ReadWrite, PixelFormat.Format16bppGrayScale);

    int stride = bmData.Stride;

    System.IntPtr scan0 = bmData.Scan0;

    Marshal.Copy(array, 0, scan0, array.Length);

    b.UnlockBits(bmData);

    //Rote the image 90 degrees

    b.RotateFlip(RotateFlipType.Rotate90FlipX);

    Image image = b;

    return image;

    }

     

    The exception is an argument exception.  Debugger points to Application.Run actually, and System.Drawing is throwing it.  It seems 16bpp grayscale isn't supported even though it pops up as an option in Intellisence, unless I'm doing something wrong.  Also all my image widths are 1472, and array is a byte[] containing pixel values (every 2 bytes represents 1 pixel).

    Losing precision is not an option, I need 16bpp grayscale.

    Friday, January 19, 2007 7:19 PM
  • If you put a breakpoint in your imageFromArray method does it step through all of the code successfully?  If you do a b.Save to save the image to disk does that work and show the image you are expecting?

     

    Mark.

    Friday, January 19, 2007 7:35 PM
  • Specifically the exception is thrown at

     

    BitmapData bmData = b.LockBits(new Rectangle(0, 0, width, height),

    ImageLockMode.ReadWrite, PixelFormat.Format16bppGrayScale);

     

    PixelFormat.Format16bppGrayScale is not a valid bitmap format.

    Friday, January 19, 2007 7:43 PM
  • If I change the pixel format to almost anything other than Format16bppGrayScale, I can get the image to load.  For instance Format16bppRgb555 works - but I don't have an RGB image, it has never been and never will be a color image.  I get a really low-contrast high-noise image that vaguely resembles the image I'm supposed to have.

    I need a way to display 16-bit grayscale images in my form.  Should I not use System.Drawing.Bitmap?

    Friday, January 19, 2007 8:55 PM
  • Hi,

      what about if you use something like 48 bits per pixel RGB image, that way you will not lose any of your grayscale information when it is in the R, G or B component, then you will have to copy the data into each channel to make it gray and you should see you original picture, granted it will be  lot larger in memory, but will still be presented correctly.

    Saturday, January 20, 2007 1:33 AM
  • Hi InfiniZac,

    I am experiencing exactly the same problem; Format16bppGrayScale does not work but other formats do. Did you ever find a solution? I'm dealing with 16 bit grayscale images as well!!

    Many thanks,
    123SB
    Thursday, October 25, 2007 8:13 AM
  • Hi,

     

    I've wasted lots of time on this same problem, and everything (other formats do work, error when I use grayscale, ect.) is the same.

     

    This is really stupid. Clearly nobody knows the solution to this yet. My data is too large to convert it all to 48bpp. Grayscale should be the easiest thing to do, not the hardest. >:/

    We can do it manually by setting all the rgb components to the same intensity for every pixel. Other than that, no ideas.

     

    Filip

    Monday, June 27, 2011 6:25 PM
  • This is an old thread.  16 bit GrayScale is handled well now.  Post to the WPF forum.
    Monday, June 27, 2011 10:05 PM
  • This is an old thread.  16 bit GrayScale is handled well now.  Post to the WPF forum.


    16 bit grayscale is handled in wpf, however the post here seems to be speaking about System.Drawing which is GDI+ specific which doesn't.

    You could try something like this:

            private static byte[] Convert16BitGrayScaleToRgb48(byte[] inBuffer, int width, int height)
            {
                int inBytesPerPixel = 2;
                int outBytesPerPixel = 6;

                byte[] outBuffer = new byte[width * height * outBytesPerPixel];
                int inStride = width * inBytesPerPixel;
                int outStride = width * outBytesPerPixel;

                // Step through the image by row  
                for (int y = 0; y < height; y++)
                {
                    // Step through the image by column  
                    for (int x = 0; x < width; x++)
                    {
                        // Get inbuffer index and outbuffer index 
                        int inIndex = (y * inStride) + (x * inBytesPerPixel);
                        int outIndex = (y * outStride) + (x * outBytesPerPixel);

                        byte hibyte = inBuffer[inIndex + 1];
                        byte lobyte = inBuffer[inIndex];

                        //R
                        outBuffer[outIndex] = lobyte;
                        outBuffer[outIndex + 1] = hibyte;

                        //G
                        outBuffer[outIndex + 2] = lobyte;
                        outBuffer[outIndex + 3] = hibyte;
                       
                        //B
                        outBuffer[outIndex + 4] = lobyte;
                        outBuffer[outIndex + 5] = hibyte;
                    }
                }
                return outBuffer;
            }


    Howard Swope
    Manager of Engineering
    TTG Media, NAVTEQ USA [http://www.navteq.com]
    Home [http://www.hms3.com]
    Tuesday, June 28, 2011 5:49 PM
  • I'm using VS 2008 though. I don't think it is handled there.

     

    Tuesday, June 28, 2011 7:49 PM
  • I'm using VS 2008 though. I don't think it is handled there. 


    Any VS later than 2005 and .Net later than 2.0.  Convert between formats using the FormatConvertedBitmap class.  No need for conversion to render.  You've resurrected a 4 year old thread.
    Tuesday, June 28, 2011 9:08 PM
  • I find that last comment kind of funny. Sorry to disturb the peace, and for being so handicapped. But thank you very much, this is helpful.

     

    Does that mean I cannot start out as Format16bppGrayScale, but rather, to use it, I would have to start out with another format and then convert it to grayscale?

    Many websites and posts, such as Howard Swope's post above, indicate that Format16bppGrayScale is not supported. I have tested it in visual studio 2008 AND 2010. I want a direct method, speed is what I am looking for.

     

    JohnWein, your comment is the only message indicating that it is supported. You also seem very sure. Note that I am using Visual Studio, System::Drawing, which is GDI+ specific. If you are correct, then I don't know what I am doing wrong. If you are wrong, I'm spending lots of extra time, but it was worth a shot.

    I also researched WPF, it is C#. Oops, i realize I'm even on the wrong forum, not to mention 4 years old thread. i'm using c++.

    Thank you so much!

    Filip



    Wednesday, June 29, 2011 4:56 AM
  • There is very little support for 16 bit grayscale in System.Drawing.  Specifically, as with indexed bitmaps, they can't be displayed directly and Graphics can't be used with them.  Actually there is very little support for images in System.Drawing, but the .Net 3.0 image support can be used to enhance the capability.   Most support for 16 bit grayscale is provided by the montor manufacturers, since standard monitors only support 8 bits.
    Wednesday, June 29, 2011 5:34 AM
  • Thank you.
    Thursday, June 30, 2011 6:46 PM

  • Converting to 8-bit is not an option.  I need all 65+K shades of gray.

    ouch i thought 50 shades of gray was allready to long.

    ;)


    you need to be on wpf for 16bppGrayScale, its not based on GDI
    • Edited by PGT2008 Tuesday, February 20, 2018 11:15 AM
    Tuesday, February 20, 2018 11:14 AM