• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 8215
  • Last Modified:

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??

0
rockyaldrich
Asked:
rockyaldrich
  • 3
  • 2
1 Solution
 
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
 
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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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
 
AlexFMCommented:
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

Featured Post

Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 3
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now