A true monochrome bitmap from C#

Is there a way to create a true monochrome bitmap from C#?  Or convert one from a 32bit or 24bit bitmap.  I got some good code from this post:
http://www.experts-exchange.com/Programming/Programming_Languages/C_Sharp/Q_21824554.html

However it behaves differently than going to MS Paint and saving as "Monochrome Bitmap".  Could this be because the conversion code uses PixelFormat.Format8bppIndexed instead of PixelFormat.Format1bppIndexed?  I tried modifiying it to use PixelFormat.Format1bppIndexed but all i get is a completely blacked out picture.  I have some picky receipt printers and they demand that the bitmap be a monochrome one.  I've looked all over and been through as much documentation as I can stand but nothing really addresses this issue.  Can anyone help??

rockyaldrichAsked:
Who is Participating?
 
AlexFMConnect With a Mentor Commented:
Yes, this is indexed bitmap with color palette which contains two color - black and white.
This book contains algorithm of converting grayscale image to black and white images:
http://homepages.inf.ed.ac.uk/rbf/BOOKS/PHILLIPS/
Chapter 3 Halftoning.
0
 
vo1dCommented:
what happens if you convert a black /white picture in your function? do the whole picture appears black?
0
 
rockyaldrichAuthor Commented:
Yes the whole thing is a black block.  And all the bitmaps I am creating are all pure white and pure black.  What I'm doing is drawing a signature captured from a pin pad device.  The sig is just a bunch of x, y coordinates and I draw that with a black pen on a white background then I need to save it as a monochrome and from what I gather using indexed colors will not work.  What I need is an unindexed 1bpp bitmap.
0
Cloud Class® Course: CompTIA Cloud+

The CompTIA Cloud+ Basic training course will teach you about cloud concepts and models, data storage, networking, and network infrastructure.

 
AlexFMCommented:
       Bitmap ConvertRGB24ToMonochrome(Bitmap source)
        {
            if ( source.PixelFormat != PixelFormat.Format24bppRgb )
            {
                return null;
            }

            Byte[] colorBytes = BitmapToBytesRGB24(source);

            if ( colorBytes == null )
            {
                return null;
            }

            bool[] monoBytes = new bool[source.Width * source.Height];

            int j = 0;

            for ( int i = 0; i < source.Width * source.Height; i++ )
            {
                // Apply threshold to average
//                monoBytes[i] = (((colorBytes[j] + colorBytes[j + 1] + colorBytes[j + 2]) / 3) >= 128);

                // Apply threshold to grayscale pixel
                byte b = (Byte)(0.3f * (float)colorBytes[j + 2] + 0.59f * (float)colorBytes[j + 1] + 0.11f * (float)colorBytes[j]);
                monoBytes[i] = (b >= 128);

                j += 3;
            }

            return BitsToBitmapMonochrome(monoBytes, source.Width, source.Height);
        }

        // Create monochrome bitmap from bool array
        Bitmap BitsToBitmapMonochrome(bool[] pixels, int width, int height)
        {
            if (pixels.GetLength(0) < width * height)
            {
                return null;
            }

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

            bmp.Palette = GetMonochromePalette();
           

            BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
                ImageLockMode.WriteOnly, bmp.PixelFormat);


            int pixelNumber = 0;

            for (int y = 0; y < bmp.Height; y++)
            {
                IntPtr linePtr = new IntPtr(data.Scan0.ToInt32() + data.Stride * y);

                for (int x = 0; x < bmp.Width; x++)
                {
                    SetBit(linePtr, x, pixels[pixelNumber++]);
                }
            }

            bmp.UnlockBits(data);

            return bmp;
        }

        void SetBit(IntPtr ptr, int pixelNumber, bool pixelValue)
        {
            int byteNumber = pixelNumber / 8;
            int bitNumber = pixelNumber % 8;

            byte b = Marshal.ReadByte(ptr, byteNumber);

            if ( pixelValue )
            {
                b |= (byte)(1 << bitNumber);
            }
            else
            {
                b &= (byte)(~(1 << bitNumber));
            }

            Marshal.WriteByte(ptr, byteNumber, b);
        }


        // Create Byte array from RGB24 bitmap
        Byte[] BitmapToBytesRGB24(Bitmap bmp)
        {
            if ( bmp.PixelFormat != PixelFormat.Format24bppRgb )
            {
                return null;
            }

            int i;
            int length = bmp.Width * bmp.Height * 3;

            Byte[] bytes = new Byte[length];

            BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
                ImageLockMode.ReadOnly, bmp.PixelFormat);

            if (data.Stride == bmp.Width * 3)
            {
                Marshal.Copy(data.Scan0, bytes, 0, length);
            }
            else
            {
                for ( i = 0; i < bmp.Height; i++ )
                {
                    IntPtr p = new IntPtr(data.Scan0.ToInt32() + data.Stride * i);
                    Marshal.Copy(p, bytes, i * bmp.Width * 3, bmp.Width * 3);
                }
            }

            bmp.UnlockBits(data);

            return bytes;
        }


        ColorPalette GetMonochromePalette()
        {
            Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format1bppIndexed);

            ColorPalette monoPalette = bmp.Palette;

            Color[] entries = monoPalette.Entries;

            entries[0] = Color.FromArgb(0, 0, 0);
            entries[1] = Color.FromArgb(255, 255, 255);

            return monoPalette;
        }
    }
0
 
AlexFMCommented:
I implemented here simplest threshold approach, take a look at the following code:

                // Apply threshold to average
//                monoBytes[i] = (((colorBytes[j] + colorBytes[j + 1] + colorBytes[j + 2]) / 3) >= 128);

                // Apply threshold to grayscale pixel
                byte b = (Byte)(0.3f * (float)colorBytes[j + 2] + 0.59f * (float)colorBytes[j + 1] + 0.11f * (float)colorBytes[j]);
                monoBytes[i] = (b >= 128);

Both versions are working, and program gives result close to Paint. I don't know exactly what algorithm is using in the Paint program, I don't think that this is threshold, it is something more complicated. If results are not sufficient, find better algorithm and implement in instead of threshold. This is called image binarization. If you want, I can post later link to free book which contains such algorithm.
0
 
rockyaldrichAuthor Commented:
I think this is still an indexed bmp.  The printer says it is a bad format, plus it's a lot smaller than when I save it with Paint.  Also the sig comes out flawed, it is broken up into wierd lines.  I wish I could attach it so you could see, it's actually pretty cool looking.  Anyways I went ahead and wrote a Win32 dll to draw the sig and flush it to disk, then I just use DllImport in my C#.  Could you please post that link to the book? I'd like to take a look at some of those algorithms and then I'll go ahead and give you the points.  Thanks for the help.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.