none
Convert Image using C# (Resolved)

    Question

  • Hi,

        This might sound like a simple thing to complete but look below...

     

    How can i Save a BMP ( image that i already have open in bitmap format) into the format Monochrome (black and white , bit depth 1)

     

    I cant seem to find a way to save a bitmap with 1 bit depth , i need this as the file size is much smaller.

     

    Many Thanks for reading and i look forward to your answers.

     

    PiXel

    Friday, May 11, 2007 4:07 PM

Answers

  • GDI+ is extremely limited support for 1bpp images.  Loading and saving, that's about it.  You can't even access the pixels of the bitmap.  Knowing something about the internal structure of those bitmaps helps to break in though.  Add a new class to your project and paste this code:

    using System;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Runtime.InteropServices;

    namespace Nobugz {
      static class Util {
        public static Bitmap BitmapTo1Bpp(Bitmap img) {
          int w = img.Width;
          int h = img.Height;
          Bitmap bmp = new Bitmap(w, h, PixelFormat.Format1bppIndexed);
          BitmapData data = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
          for (int y = 0; y < h; y++) {
            byte[] scan = new byte[(w + 7) / 8];
            for (int x = 0; x < w; x++) {
              Color c = img.GetPixel(x, y);
              if (c.GetBrightness() >= 0.5) scan[x / 8] |= (byte)(0x80 >> (x % 8));
            }
            Marshal.Copy(scan, 0, (IntPtr)((int)data.Scan0 + data.Stride * y), scan.Length);
          }
          return bmp;
        }
      }
    }

    Call it like this:

          Bitmap bmp = Nobugz.Util.BitmapTo1Bpp(new Bitmap(@"c:\temp\test1.bmp"));
          bmp.Save(@"c:\temp\test.bmp", System.Drawing.Imaging.ImageFormat.Bmp);

    A few remarks with this code.  Sadly, I couldn't avoid the pointer arithmetic in the Marshal.Copy() call, the code is not going to work in 64-bit apps.  And it is not going to be terribly fast, Bitmap.GetPixel() is slow.  Hope it works for you.
    Friday, May 18, 2007 6:29 PM
    Moderator
  • I believe you answer lies in manipulating the pallet/bitdepth of the image before saving it or using the proper paramaters of

    Image.Save Method (String, ImageCodecInfo, EncoderParameters)


    I beleive ImageCodecInfo would allow you to set the file type and EncoderParameters would allow you to set the ColorDepth
    Friday, May 11, 2007 5:04 PM
  • Hi, Pixel

    You can use ColorMatrix to make the image file to be Monochrome, follows the sample code:

             

    Code Snippet

    Bitmap UrBitmap = new Bitmap(@"Source File name");
                float[][] ptsArray ={
    new float[] {0.5f, 0.5f, 0.5f, 0, 0},
    new float[] {0.5f, 0.5f, 0.5f, 0, 0},
    new float[] {0.5f, 0.5f, 0.5f, 0, 0},
    new float[] {0, 0, 0, 1, 0},
    new float[] {0, 0, 0, 0, 1}  };
               ColorMatrix clrMatrix = new ColorMatrix(ptsArray);

              ImageAttributes imgAttribs = new ImageAttributes();
               imgAttribs.SetColorMatrix(clrMatrix, ColorMatrixFlag.Default,  ColorAdjustType.Default);

              UrBitmap.Save(@"Destination File name");

     

    Thanks

    Tuesday, May 15, 2007 6:46 AM

All replies

  • I believe you answer lies in manipulating the pallet/bitdepth of the image before saving it or using the proper paramaters of

    Image.Save Method (String, ImageCodecInfo, EncoderParameters)


    I beleive ImageCodecInfo would allow you to set the file type and EncoderParameters would allow you to set the ColorDepth
    Friday, May 11, 2007 5:04 PM
  • Hi, Pixel

    You can use ColorMatrix to make the image file to be Monochrome, follows the sample code:

             

    Code Snippet

    Bitmap UrBitmap = new Bitmap(@"Source File name");
                float[][] ptsArray ={
    new float[] {0.5f, 0.5f, 0.5f, 0, 0},
    new float[] {0.5f, 0.5f, 0.5f, 0, 0},
    new float[] {0.5f, 0.5f, 0.5f, 0, 0},
    new float[] {0, 0, 0, 1, 0},
    new float[] {0, 0, 0, 0, 1}  };
               ColorMatrix clrMatrix = new ColorMatrix(ptsArray);

              ImageAttributes imgAttribs = new ImageAttributes();
               imgAttribs.SetColorMatrix(clrMatrix, ColorMatrixFlag.Default,  ColorAdjustType.Default);

              UrBitmap.Save(@"Destination File name");

     

    Thanks

    Tuesday, May 15, 2007 6:46 AM
  • Hi,

     

    IsshouFuuraibou :

     

    How do you fill these parameters ?

     

    Figo Fei - MSFT  :

     

    How does this code work ? it looks like it is just opening the image and saving it , with some unrelated code inbetween?

     

    Many Thanks

     

    Jason

    Friday, May 18, 2007 2:17 PM
  • GDI+ is extremely limited support for 1bpp images.  Loading and saving, that's about it.  You can't even access the pixels of the bitmap.  Knowing something about the internal structure of those bitmaps helps to break in though.  Add a new class to your project and paste this code:

    using System;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Runtime.InteropServices;

    namespace Nobugz {
      static class Util {
        public static Bitmap BitmapTo1Bpp(Bitmap img) {
          int w = img.Width;
          int h = img.Height;
          Bitmap bmp = new Bitmap(w, h, PixelFormat.Format1bppIndexed);
          BitmapData data = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
          for (int y = 0; y < h; y++) {
            byte[] scan = new byte[(w + 7) / 8];
            for (int x = 0; x < w; x++) {
              Color c = img.GetPixel(x, y);
              if (c.GetBrightness() >= 0.5) scan[x / 8] |= (byte)(0x80 >> (x % 8));
            }
            Marshal.Copy(scan, 0, (IntPtr)((int)data.Scan0 + data.Stride * y), scan.Length);
          }
          return bmp;
        }
      }
    }

    Call it like this:

          Bitmap bmp = Nobugz.Util.BitmapTo1Bpp(new Bitmap(@"c:\temp\test1.bmp"));
          bmp.Save(@"c:\temp\test.bmp", System.Drawing.Imaging.ImageFormat.Bmp);

    A few remarks with this code.  Sadly, I couldn't avoid the pointer arithmetic in the Marshal.Copy() call, the code is not going to work in 64-bit apps.  And it is not going to be terribly fast, Bitmap.GetPixel() is slow.  Hope it works for you.
    Friday, May 18, 2007 6:29 PM
    Moderator
  • Oops, I forgot this statement, insert it just before "return bmp":

      bmp.UnlockBits(data);

    Friday, May 18, 2007 6:31 PM
    Moderator
  • A bitmap with 1 bit depth may not be as small as a compressed (jpeg or gif or png) file of the same image.  Space, both memory and storage, is so abundant today that converting an image to save space is usually not necessary.
    Friday, May 18, 2007 9:10 PM
  • Nice comment John, you dissed 4 people with one throw-away statement.  The OP might just use it to convert TIFFs created by a document scanner.  If not, somebody else might.
    Friday, May 18, 2007 9:49 PM
    Moderator
  • I wholeheartedly disagree with this statement.

    While it may be true that some compressions are better than 1 bit depth uncompressed files, Space, both memory and storage, is not guaranteed to be abundant. There are situations and applications where storage is limited, and where for whatever reason requires an uncompressed file format. Or maybe the reason is because of a requirement for 1 bit imagery to be used. Making a broad dismissal claim like that is terrible.

    Just because something is abundant, or perceived to be, doesn't mean you shouldn't be efficient with what you use. That seems to be a horrid trend in thinking that since there is an abundance of space/processing power/etc that it's alright to write inefficient programs or use the space inefficiently.
    Friday, May 18, 2007 10:15 PM
  • nobugz:

    You made my feel bad hurt.  I was only addressing this line from the OP post:

    I cant seem to find a way to save a bitmap with 1 bit depth , i need this as the file size is much smaller.

    I thought all I was saying was that a compressed file might be as small as a 1 bit bitmap file. 

    Friday, May 18, 2007 10:36 PM
  • Yah, Phil Katz would give his left testicle for a guaranteed 24 : 1 loss-less compression ratio.  May he rest in peace.
    Saturday, May 19, 2007 12:49 AM
    Moderator
  • I sorry my English not so good. 

    Here is an example of the point I was trying to make:

    I used nobugz's code to transform a screen shot which was 1920 X 1200 x 32bpp.

    The 32bpp .bmp file size was 9,216,054.

    The .png file size was 180,588.

    The 1bpp .bmp file size was 288,062.

    I would archive the .png file.  I would always have the original file to convert to whatever format I might need.

    My point is that you may not save space by converting to 1bpp.  The loss of information by converting to 1bpp to save space is just not justified. 

    I don't see how any of this has any effect upon the posting of code to convert to 1bpp.  Certainly there is a need for monochrome bitmaps.

    Saturday, May 19, 2007 6:20 AM
  • Hi,

     

    Thanks alot! That code works a treat.

     

    I know there is other image formats which would be more practical but we need the image in bmp and in 1 bit depth as above. This is for storing into a old oracle database and extracting into a report (crystal).

     

    Many Thanks for all your help.

     

    Jason

    Monday, May 21, 2007 9:00 AM
  •  

    Thankx for the class nobugz, works like a charm.

     

    Here is the full class (with 'bmp.UnlockBits(data);' added) for easy copy/paste...

     

    Code Snippet

    using System;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.Runtime.InteropServices;

    namespace Nobugz

    {

        static class Util

        {

           public static Bitmap BitmapTo1Bpp(Bitmap img)

           {

               int w = img.Width;

               int h = img.Height;

               Bitmap bmp = new Bitmap(w, h, PixelFormat.Format1bppIndexed);

               BitmapData data = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);

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

               {

                   byte[] scan = new byte[(w + 7) / 8];

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

                   {

                       Color c = img.GetPixel(x, y);

                       if (c.GetBrightness() >= 0.5) scan[x / 8] |= (byte)(0x80 >> (x % 8));

                   }

                   Marshal.Copy(scan, 0, (IntPtr)((int)data.Scan0 + data.Stride * y), scan.Length);

               }

               bmp.UnlockBits(data);

               return bmp;

           }

        }

    }

     

     

    Tuesday, May 20, 2008 2:55 AM
  • Hi 

    could explain the lines:

    byte[] scan = new byte[(w + 7) / 8];
    // Why (w + 7) / 8 ?

    and


     if (c.GetBrightness() >= 0.5) scan[x / 8] |= (byte)(0x80 >> (x % 8)); 
    I dont understand :
                     1) x / 8
                     2) |= (byte)(0x80 >> (x % 8)
    )

    thank you

    Thursday, September 25, 2008 4:23 PM
  • (w + 7) / 8: how many bytes for w bits.
    x / 8: the scan[] index for bit #x
    0x80 >> (x % 8): byte mask to turn bit #x on.


    Hans Passant.
    Thursday, September 25, 2008 9:16 PM
    Moderator
  • Thank you for your quick reply

    Plase..  I have other question

    what is meant by |= ? (I know +=, -=, etc, but it operator is the first time I see)

    Or 

    where can I find documentation on this?


    Thanks again nobugz
    Thursday, September 25, 2008 11:55 PM
  • Any book about the C# language will tell you this.
    Hans Passant.
    Friday, September 26, 2008 12:00 AM
    Moderator
  • I hope my people here were helpful!


    http://fakesteveballmer.blogspot.com

    MS CEO - god to you people
    Friday, September 26, 2008 4:20 AM
  • Hi 

     

     

    could explain the lines:

    byte[] scan = new byte[(w + 7) / 8];
    // Why (w + 7) / 8 ?

    and


     if (c.GetBrightness() >= 0.5) scan[x / 8] |= (byte)(0x80 >> (x % 8)); 
    I dont understand :
                     1) x / 8
                     2) |= (byte)(0x80 >> (x % 8)
    )

    thank you

     


    Hiii can you please suggest me the changes to be made to convert this code to return a bitmap
    having 8 bits per pixel?

    Thanks

    Thursday, July 30, 2009 12:08 PM
  • It's working fine but i am not getting good images .some part of images are  lost . How can i solve this.

    Thanx IN advance

    Tuesday, August 03, 2010 5:00 AM
  • you can use MultiDll and convert images
    Sunday, September 30, 2012 11:52 AM