Use GDI+ to Save Crystal-Clear GIF Images with .NET

In an effort to make our gate camera images visible on a ridiculously small cellphone screen,  I spent the past couple of days learning about the process of image Quantization.   GDI+ will allow you to take a full-color 32 bpp image and save it as a .gif file!  This turns out to be quite simple, and you can even re-size the image before saving it, by using the CreateThumnailImage method.

System.Drawing.Bitmap b = new System.Drawing.Bitmap(“c:\\original_image.gif“);
System.Drawing.Image thmbnail = b.GetThumbnailImage(100,75,
null,new IntPtr());
thmbnail.Save(“c:\\thumnail.gif“, System.Drawing.Imaging.ImageFormat.Gif);

“Cool!“ You’re thinking to yourself, but don’t think too soon.  You may be less-than-impressed by the dithery, grainy image produced by this technique.  Here’s the output of this method with one of our gate camera images: 

So, what accounts for the grainy image?  It has to do with color quantization, sometimes called “palettization” or simply, the process of slapping a 256 color palette onto a full-color image, thereby reducing the total colors (and file-size).  The image is grainy because GDI+ by default uses the windows 256-color palette, and doesn’t take into account the colors in the actual image.  When I found this out I went in search of a better color quantization technique for saving .GIF images… 

Personally I’ve found that almost all GIF images should be saved with an adaptive palette. These days, forget about the Web Palette, unless you’re doing icons or big areas of solid color.  Most everyone will be running in at least 16K colors, and if by some small chance they’re still running 256 colors, the browser will dither an adaptive image to a reasonable version anyhow.  An adaptive palette contains the 256 most frequently used colors in the image. Well, um sort of anyway. (As an aside, I tried to write my own “popularity palette” algorithm which used the most frequently used colors to build the palette, but my images looked worse than before!)  A good quantization algorithm also takes into account spacing between colors and stores colors that are, say blue and very-nearly-blue as one color, freeing up more space for colors that the eye sees as separate.  One such adaptive palette algorithm is called the “Octree“ algorithm.

I found two articles from Microsoft that helped out a great deal, KB 319061  and Optimizing Color Quantization for ASP.NET Images by Morgan Skinner at MS.  Morgan’s article had some great code samples, and really useful base class which will allow you to plug in your own algorithm to quantize images. I ended up pulling bits and pieces of code from the two articles to create a library of objects used for creating Octree and Grayscale palettes.  Using the OctreeQuantizer object was easy, as you can see in this code snippet:

System.Drawing.Bitmap b = new System.Drawing.Bitmap(“c:\\original_image.gif“);
System.Drawing.Image thmbnail = b.GetThumbnailImage(100,75,
null,new IntPtr());
OctreeQuantizer quantizer =
new OctreeQuantizer ( 255 , 8 ) ;
using ( Bitmap quantized = quantizer.Quantize ( thmbnail ) )
{
     quantized.Save(“c:\\thumnail.gif“, System.Drawing.Imaging.ImageFormat.Gif);
}

OctreeQuantizer grayquantizer = new GrayscaleQuantizer ( ) ;
using ( Bitmap quantized = grayquantizer.Quantize ( thmbnail ) )
{
     quantized.Save(“c:\\thumnail.gif“, System.Drawing.Imaging.ImageFormat.Gif);
}

This code produced the following beautiful (In this case, beauty truly is in the eye of the guy who just spent three days trying to get it to work!) thumbnails, one with an adaptive palette, and one in grayscale. 

If you’ve got a cellphone with web capabilities, go to http://mobile.vit.org/ and see the image in all its 100 X 75 pixel glory.

UPDATE: 

See this post here for the latest version of this library

 

-Brendan

 

About Brendan Tompkins

Brendan runs CodeBetter.Com. He was twice awarded MVP for Microsoft .NET, and is a founder and the CTO of Quick180.Com More about Brendan at https://www.linkedin.com/codebetter
This entry was posted in ASP.NET Tutorials, Most Popular. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Peter

    Thank you for the code! I managed to find a solution for keeping transparency after edit, with the following code (C#)

    public class OctreeQuantizer : Quantizer
    {
          ………………….
            protected override byte QuantizePixel(Color32 pixel)
            {
                byte paletteIndex = (byte)_maxColors;    // The color at [_maxColors] is set to transparent

                // Get the palette index if this non-transparent
                if (pixel.Alpha > 0)
                    paletteIndex = (byte)_octree.GetPaletteIndex(pixel);

                return paletteIndex;
            }

            protected override ColorPalette GetPalette(ColorPalette original)
            {
                // First off convert the octree to _maxColors colors
                ArrayList palette = _octree.Palletize(_maxColors – 1);

                // Then convert the palette based on those colors
                for (int index = 0; index < palette.Count; index++)
                {
                    Color TestColor = (Color)palette[index];
                    //Test set transparent color when color transparency used
                    if (TestColor.ToArgb() == Color.Transparent.ToArgb())
                    {
                        TestColor = Color.FromArgb(0, 0, 0, 0);
                    }
                    original.Entries[index] = TestColor;
                }
                //Clear unused palette entries
                for (int index = palette.Count; index < _maxColors; index++)
                {
                    original.Entries[index] = Color.FromArgb(255, 0, 0, 0);
                }
                // Add the transparent color when alpha transparency used
                original.Entries[_maxColors] = Color.FromArgb(0, Color.Transparent);
                return original;
            }
          ………………….
    }

    public abstract class Quantizer
    {

          ……………………..

    protected virtual void SecondPass(BitmapData sourceData, Bitmap output, int width, int height, Rectangle bounds)
            {
                  ……………………………

     
                            // Check if this is the same as the last pixel. If so use that value
                            // rather than calculating it again. This is an inexpensive optimisation.
                            if (Marshal.ReadInt32(pPreviousPixel) != Marshal.ReadInt32(pSourcePixel))
                            {
                                // Quantize the pixel
                                pixelValue = QuantizePixel(new Color32(pSourcePixel));

                                // And setup the previous pointer
                                pPreviousPixel = pSourcePixel;
                            }

                  ……………………………
           }

          ……………………..

    }
     

  • http://www.youtubeizle1.com youtube

    thanks a lot

  • http://www.oyunambari.com Oyun

    Great — thanks for responding. I will plug it in and see how it goes..Again Thanks

  • http://www.youtube7.net youtube

    The “Optimization…”article at the MS site has moved, to here;

  • http://www.balikavi.info balık avı

    süperrrrrrrrrrrrr

  • http://msdn2.microsoft.com/en-us/library/aa479306.aspx st3v3n

    The “Optimization…”article at the MS site has moved, to here;
    http://msdn2.microsoft.com/en-us/library/aa479306.aspx

  • http://www.tercumehizmeti.com Tercüme

    Thanks

  • http://youtube.oku.gen.tr youtube

    Tried the assembly (downloaded latest dll). Works fine on a local box. But on shared server end up with the error on:

    Dim quantizer As OctreeQuantizer = New OctreeQuantizer(255, 4)

    System.Security.SecurityException: Request for the permission of type ‘System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′ failed…..

  • http://www.eceoyun.com kral oyun

    Great code, saved me from trying to do the same thing myself and wasting hours of time!
    Thanks!….

  • http://www.eceoyun.com kral oyun

    Great code, saved me from trying to do the same thing myself and wasting hours of time!
    Thanks!

  • http://www.mersinim.net mersin

    Dim quantizer As OctreeQuantizer = New OctreeQuantizer(255, 4)

    System.Security.SecurityException: Request for the permission of type ‘System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′ failed.

  • http://www.mersinim.net mersin

    thaks

  • Greg

    Tried the assembly (downloaded latest dll). Works fine on a local box. But on shared server end up with the error on:

    Dim quantizer As OctreeQuantizer = New OctreeQuantizer(255, 4)

    System.Security.SecurityException: Request for the permission of type ‘System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′ failed.

    Using in ASP.NET.

  • http://www.islamiruyatabirleri.com Rüya Tabirleri

    what is the problem?
    what is this posts? spam?

    why dont you delete it?

  • AR

    this is great stuff, however i am facing an issue, can someone help. I am using this quantizer to save a bitmap image into a gif format (so that i donot loose the quality and the image is small), however for images with some specific colors it gives weird results, like for example if the color is Red, half of the image is good and the second half turns black. It also happens with some other colors like Cyan, Blue etc. Has someone faced this issue, can you guys point me in some direction to debug this.

  • http://www.kral27.com barbie oyulari

    yes it is

  • http://www.verycomics.com hugo oyunlari

    l love dhis blog it is very nice

  • Gretchen

    Thank you so much for sharing your wonderful work with ‘us’. My charts are now perfect!

  • Andy

    I have tried to implement some of the suggested transparent gif solutions but did not get any of them to work

    I can use the one described by microsft at
    http://support.microsoft.com/kb/319061

    But this is not giving me the results I was hoping for… Does anyone know how to flatter non transparent pixels that still have an alpha value to a matte color and then cut the rest of the out? Sort of like Photoshop’s saving of a gif?

    brendan Do you have version that works with transparencies?

  • Roy Thomas

    Can anybody advise vb or vc code to refresh a real time (Gif file) from a website and archive it in the local machine every one minute, Can it be done using using Microsoft web browser control?

    please email to roy.thomas@cegelec.com.sg

    thanks

  • http://barbaros1420@hotmail.com kral oyunlar

    Thank you!.

  • http://www.in-gender.com Maureen

    I think I love you! This worked perfectly — now my gif’s are beautiful and I can remove the annoying disclaimer that “this preview image is grainy but your final product will be lovely”.

  • http://www.timdown.co.uk Tim Down

    Just wanted to add my own thanks to the many who have posted theirs. This is absolutely perfect for what I need – it’s fixed both my problems, namely that I was jumping through hoops to get transparency and that the default colour palette was making my images look terrible. So, many thanks.

  • liz

    everytime i go to save a picture it always saves under ” bmp ” and not ” gif ” so the picture doesn’t move when it’s suppose to !!

  • Andrew

    I’m also having a permissions error in a medium trust environment (shared server hosting environment).

    Can’t we have a fix for this please? It’s such a great library! Best there is out there, just this one little problem preventing it from being more widely used. :(

  • http://futurefog.altervista.org Marco

    GIF TRANSPARENCY… ANOTHER SOLUTION:

    First of all, thanks for this great work!

    Second, I’ve found a solution for preserving GIF transparency when invoking Image::Save(…). The .NET (tested on v2.0) GIF encoder considers transparent the first color found in the palette, so I’ve changed some methods (replace them with the supplied ones):

    // METHOD: OctreeQuantizer::QuantizePixel(Color32* pixel)

    protected override byte QuantizePixel(Color32* pixel)
    {
    byte paletteIndex = (byte)0; // The color at [0] is set to transparent

    // Get the palette index if this non-transparent
    if(pixel->Alpha > 0)
    paletteIndex = (byte)(_octree.GetPaletteIndex(pixel) + 1);

    return paletteIndex;
    }

    // METHOD: OctreeQuantizer::GetPalette(ColorPalette original)

    protected override ColorPalette GetPalette(ColorPalette original)
    {
    // First off convert the octree to _maxColors colors
    ArrayList palette = _octree.Palletize(_maxColors – 1);

    // Add the transparent color
    palette.Insert(0, Color.FromArgb(0, 0, 0, 0));

    // Then convert the palette based on those colors
    for(int index = 0; index < palette.Count; index++)
    original.Entries[index] = (Color)palette[index];

    return original;
    }

    // CONSTRUCTOR: GrayscaleQuantizer::GrayscaleQuantizer()

    public GrayscaleQuantizer()
    : base(new ArrayList())
    {
    _colors = new Color[256];

    int nColors = 256;

    // Initialize a new color table with entries that are determined
    // by some optimal palette-finding algorithm; for demonstration
    // purposes, use a grayscale.
    for(uint i = 0; i < nColors; i++)
    {
    uint Alpha = (uint)((i == 0) ? 0×00 : 0xFF); // Colors are opaque (except the first).
    uint Intensity = Convert.ToUInt32(i * 0xFF / (nColors – 1)); // Even distribution.

    // The GIF encoder makes the first entry in the palette
    // that has a ZERO alpha the transparent color in the GIF.
    // Pick the first one arbitrarily, for demonstration purposes.

    // Create a gray scale for demonstration purposes.
    // Otherwise, use your favorite color reduction algorithm
    // and an optimum palette for that algorithm generated here.
    // For example, a color histogram, or a median cut palette.
    _colors[i] = Color.FromArgb((int)Alpha,
    (int)Intensity,
    (int)Intensity,
    (int)Intensity);
    }
    }

  • http://www.bluwavedesign.co.uk/ Matt Eason

    Fantastic stuff. Dropped it into my project and it worked first time, no problems. Thanks!

  • Andrey

    The best! Thank you very much for this!

  • http://best-soft.ru/ ca1

    Thank you!

  • Pablo

    Eres el puto amo!

    You’re the master, you saved us many hours.

    Thanks from Spain!

  • RS

    So, how do I make this work:

    1. Include the dll (tried both the pre-built version and my own version) in my own web-project.
    2. Build error: Failed to grant minimum permission requests.

    But I can’t even find where to set permissions (in the project, or IIS, or?) ?

    Thanks!
    RS

  • James Maeding

    there was code to modify the palette posted by Fred Thieme, see previous posts.
    I am currently figuring out how to plug that into things, I am a bit new at the .net objects. I’ll post it when I figure it out.

  • http://www.somej.net Justin Wignall

    Three years later and still really useful! Great stuff Brendan, Thanks!

  • Allan The Dane

    Preserving Transparency?

    I’m converting a transparent Gif to grayscale using you code. But the transparency seems to be lost during conversion. Is there any way to preserve the transparency in the new image? I’m using the following code, which is basically just a conversion from your C# code to VB:

    Dim objBitmap As New Bitmap(“Transparent.Gif”)
    Dim objNewThumb As Drawing.Image = objBitmap.GetThumbnailImage(objBitmap.Width, objBitmap.Height, Nothing, New IntPtr())
    Dim objGrayQuantizer As New GrayscaleQuantizer()
    With objGrayQuantizer.Quantize(objNewThumb)
    .Save(“GrayNoSoTransparent.gif”)
    End With

    Any advice would be appreciated.

    BTW – Great work! :)

    Allan From Denmark

  • James Maeding

    ok, had to check the allow unsafe code in build options,
    also added suggested changes myself except for transparent option.
    If anyone had improved things, please post :)

  • James Maeding

    I just read the previous comments and am wondeeing if the code and dll that we can download, have all the fixes in them?
    There was the definitions of _red, _green, _blue and _pixelcount from “int” to “ulong”,
    as well as the Transparent palette code (bug or feature?…).

    Also, I open the code in VS2005 and it gives errors on build. It says:
    Unsafe code may only appear if compiling with /unsafe.

    I assume I need to add a flag somewhere to fix that, I think the orig code was from VS2003, and VS2005 wants a flag…
    thanks

  • James Maeding

    Dang, best code I have seen for images with .net so far.
    I am killing myself with IMageMagick and other command line progs, and no one seems to make it easy to make good images from 24 bit to 8 bit optimized palette.
    This was just what I needed.

  • Manso

    Hi,

    Thanks for an excellent component.

    We’re trying to use it in a medium trust environment, does anyone have any experience in how to make it work? We are getting “PolicyException: Required permissions cannot be acquired.”, can someone provide us with some hints if/how we can make this work?

    Many thanks,
    Manso

  • Anonymous

    this guy is selling component based on OctreeQuantizer.cs

    http://www.amarantin.se/

  • Tuan Nguyen

    can anyone please post the safe managed Code for this?

  • Barry

    Hey Brendan. It has been snowing here in CANADA. Looks like it isn’t there!
    Thanks for the reply!!
    That’s great that the code can do it!!

    Which lines of code are required to do what I want?
    Can you help me set it up?
    I see some lines at the top starting with:
    System.Drawing.Bitmap b = new System.Drawing.Bitmap(“c:\\original_image.gif“);

    I also downloaded your project, do I need it all?
    Is there an aspx page missing?

    Which files/folders do I need to upload to my web server to get this to work?
    I’m a slow newbie.

    Please help.

    Barry

  • btompkins

    Barry,

    The quantizer code should produce a 16-color gif using the following command:

    OctreeQuantizer quantizer = new OctreeQuantizer ( 15 , 4 ) ;

  • Barry

    Hello.

    ***I am a newbie who needs help! ***

    This looks like the perfect program I would need to convert a 256 color GIF that is located on my web server in a folder called temp, into a 16 color GIF.
    After it is converted, I want to save it with the same name and path.
    I have Visual Web Developer 2005 Express.
    Please help me set this up.
    I have no .aspx file yet.
    (Also, I have no experience yet).
    I will be coming from an asp page that has the filename in the url like my_page.asp?file_name=image_256.gif
    I am used to vbscript, so I would get the filename with:
    file_name= Request.QueryString(“file_name”)

    I have placed all files (from the download above) in a web folder called gif_16
    Any help to figure this out would be appreciated.

    Thanks in advance!!!
    Barry in Ottawa, CANADA.

  • Shaun Llewelyn

    Thanks, I was getting grainy gifs. Worked a treat. Brilliant!

  • http://www.ellisys.com Sylvain Fasel

    Hi,

    thank you very much for this wonderful piece of code, seeminglessly integrated into my project.

    For what I need it to do, it works perfectly, and the images are perfect.

    So cool to share this freely!

    Best regards.

  • Ariel

    I tried doing it at the end of the drawing, just before saving and it improves the quality, but no transparent background.. plz can some1 ask me as soon as possible? I really need it

  • Ariel

    I tried doing it at the end of the drawing, just before saving and it improves the quality, but no transparent background.. plz can some1 ask me as soon as possible? I really need it

  • Ariel

    Hi brendan, i congratulate you for your project.. i’m trying to use it but i can’t make it work.. i’m creating dynamic images with text (drawstring with a Graphics), and i’m not sure where to use the quantizer, if i modify the bitmap before the “Graphics graph =Graphics.FromImage(bm)” it throws an exception liek this: “A Graphics object cannot be created from an image that has an indexed pixel format. “, if i put it after then it remains black at the end..
    Just in case i post the function i made to create the Graphics:
    public Graphics openGraph(ref Bitmap bm,string bgcolor)
    {
    if(bgcolor==”Transparent”)
    {
    OctreeQuantizer quant=new OctreeQuantizer(255,8);
    bm=quant.Quantize(bm);
    }
    Graphics graph = Graphics.FromImage(bm);
    graph.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit;graph.InterpolationMode=System.Drawing.Drawing2D.InterpolationMode.Bilinear;
    graph.Clear(Color.FromName(bgcolor));
    return graph;
    }

    (i’ve already tried this one with it too:
    “bm.MakeTransparent(Color.FromName(drpBackcolor.SelectedItem.Text));
    bm.Save(……)”)

    but nothing..plz if someone can answer me, i’d appreciate that thx..(if you can, mail me plz to flesler@hotmail.com)

  • http://www.regthesledge.net Will

    Thanks a million.

    Like many others here, I was reluctantly bracing myself to do battle with the MSDN code and you’ve saved me ages.

  • Steve

    Hello,

    I really would like to be able to use you code but Im not sure if I can. So far I have an image being displayed using a .ashx file. This file basicaly gets an jpeg image from a database and makes it into a trnasparent gif but obviously the gif looks terrible.

    Can I use your code? And if so how? I’ve never used a .dll file like this before.

    Thanks in advance,
    Steve

  • Chris K

    Hello, this stuff is awesome. Fortunatly I found this page, when this problem occured in my program.

    What i wanted to ask: Is there any solution to save partly transparent (Opacity: 50%, or something like this) areas? pixels with alpha=255 are saved transparent, but when the alpha value is less then 255, these pixel are black!

    Chris

  • Jimbob

    Great stuff mate. I would have expected this to be part of GDI+ to be honest, as MS have done such a good job with the rest of it.

    Thanks again.

  • http://fromchina xuefeng yu

    this is gread work!!!
    thanks

  • Sebastian Krampe

    Thanks for the classes, Brendan. They work great!

    And Fred: Thanks to you for the transparent palette method.
    Here’s the version in c#:

    ColorPalette SetTransparentColor(ColorPalette SrcPal, int nColors, System.Drawing.Color cT)
    {
    System.Drawing.Color C;
    System.Drawing.Color[] Clrs = SrcPal.Entries;
    for (int i = 0; i < nColors; i++)
    {
    C = Clrs[i];
    if (C.R == cT.R && C.G == cT.G && C.B == cT.B)
    {
    SrcPal.Entries[i] = Color.FromArgb(0, C.R, C.G, C.B);
    }
    else
    {
    SrcPal.Entries[i] = Color.FromArgb(255, C.R, C.G, C.B);
    }
    }
    return SrcPal;
    }

    Regards
    Sebastian

  • Lee

    Tried the original dll as well as this dll, all works beautifully on my machine locally, when I uploaded to my hosting, permission deneid. I’m guess the Quantizier is referencing system managed classes.. darn. what a shame.

    really really loved this class.

  • http://www.abdicate.net Bill

    I’ve been working on my site to generate Hebrew characters and though I could force someone to download a font to view them, I wanted to dynamically generate the images. You’re DLL is the best and got rid of those pesky “white” dots around the characters. Do you have the code in VB? Thanks,

    Bill

  • http://codebetter.com/blogs/brendan.tompkins Brendan Tompkins
  • srounsroun

    lol, sorry I found it on the zip file up there

    check in bin/debug to get the DLL.

    by all and thanks a lot

  • srounsroun

    Hi,

    I’m working on gif files, the link for the dll doesn’t work. Can someone could send me thte .zip file in my hotmail box ? thanks a lot, Hope this will resolve my optimization problem :)

    here my hotmail box: srounsroun@msn.com

    thanks

  • phil

    Using Quantizer to reduce size of bitmap when saved to file. Works perfectly. Thanks.

  • Dave

    This is awesome. Just about to try to write my own version, found this site, five minutes later I have crystal clear images.

    Thank you.

  • Yukiko

    I still not quite understand the whole thing and how to use it after downloading? Can someone please help me?

  • Ganesha

    Thank you for the dll, my map image looks better now. Best regards from Jakarta.

  • Peter M

    First of all, thanks very much for your efforts. This code was exactly what I’ve been looking for. :)

    I’m using it to return gif images through the Response.OutputStream of an aspx page. The weird thing is that the transparency is working fine with some images and not with others although they all come from the same source.

    Any thoughts would be greatly appreciated.

  • Max

    Well done! I had little-to-no trouble implementing this into my system.

    (What I want to know is why the current version of .NET doesn’t already support this properly — this seems like a common thing to do with graphics!)

  • http://www.sapphire-erm.com Hubris Sonic

    niiiiiiiiiiiiiiiice dude!

  • Bill Oscott

    Hi

    This dll is cool, I had program running in VB.net that accessed its functions in 5mins. Thing is I would also like to use VB6 to access the dll. Is there anything I can do??

    Thanks

    Bill

  • Daniel

    Is there a smart way to crop a created image, to eliminate all the space surrounding the rendered text?

    "objGraphic.MeasureString(sText, objFont).Width" usually adds a lot of space, especially when using large font sizes. That extra margin causes rendered headlines to not line up well with body text.

  • http://codbetter.com/blogs/brendan.tompkins Brendan Tompkins

    John,

    Which Quantizer did you modify? The correct way to do this would have been to create a new class deriving from Quantizer called FourBitQuantizer or something, and then modify the QuantizePixel and InitialQuantizePixel methods.

    I’m not surprized that it cut the image in half and only rendered every other line. Everything was based on 8 and you substitued 4!

  • http://john.hamm.com John Hamm

    I meant to say "every other vertical column"

  • http://john.hamm.com John Hamm

    I have modified the Quantizer class in that I can now pass the Quantize method a PixelFormt parameter so I can produce 4-bit images.

    The problem, though, is that I think there is something odd going on in the OctreeQuantizer class – the bitmap that is produced has every other vertical row blank, and that widens the image, and cuts off half the image.

    I think the problem may lie somewhere in FirstPass and SecondPass methods, I don’t know enough about it to be sure though. Is traversing the pixels the same in a 4-bit image in memory as it is an 8-bit image?

    Any ideas?

  • http://www.sageoak.com Doug Weems

    Thanks Brendan,

    This was very helpful. You are one of the good guys. :-)

  • Chris Lewis

    …have been searching for some time for what you’ve done here – thanks a lot.

  • brendan – you’re a star

    thanks mate, just what i needed…and better than i was looking for.

  • Brendan Tompkins

    Murray, the unsafe is there so that we can pass a pointer to the quantizer ie (Color32* pixel) I’m not sure how to rework this in managed code without looking and getting into the details. You could try passing the Color32 by ref, and see where you get.

    -Brendan

  • Murray

    Cool

    But what if my hosting environment doesn’t like unsafe code? is there a safe version?

    I realise it may be slower, but it only runs infrequently as a batch job.

    Cheers.

  • Hugo

    Thanks and thanks again! I’ve been trying to find something like this for days!

  • Pasquale Alfonso (aka)Bklynjava

    Nice work, although I have numerous errors opening with C#.

    Any advice is appreciated.

  • Ross Presser

    Did you ever grab my updated code? If not, could you email your email address to me at rpresser AT imtek DOT com so I can mail it to you?

  • Brendan Tompkins

    Nope. Thanks is plenty!

  • Anthony Ruggeri

    Great work on this library, it saved me a ton of time adapting an automatic graphics generator from using PNGs to GIFs due to a color interpretation bug in IE6. Let me know if you take any payments or contributions!

  • Ross Presser

    Can’t find an email address for you … grab it at http: // http://www.imtek.com /images/updatedcode.zip [remove spaces to fix the deliberately broken link I typed]

  • Brendan Tompkins

    Ah… Cool. Can you send me the code that you updated, so that I can post it?

    Thanks!

  • Ross Presser

    Maybe this never cropped up before because I was just using larger images than you’re used to. My images are 1500×1650 pixels; they’re geographical maps that we put into trip routing books that you get from your auto club.

  • Ross Presser

    I was right. Recompiling with /check+ and running my test case caused it to throw an arithmetic overflow exception in OctreeNode.Increment. I changed the definitions of _red, _green, _blue and _pixelcount from "int" to "ulong", and made this change to compensate, in OctreeNode.ConstructPalette:

    // And set the color of the palette entry

    palette.Add ( Color.FromArgb ( (int)(_red / _pixelCount) , (int)(_green / _pixelCount) , (int)(_blue / _pixelCount) ) ) ;

    Now it works on all my images.

    Thanks again for the code!

  • Brendan Tompkins

    Man, that’s strange. Looks like possibly the image is corrupt, and it’s trying to read image data where it can’t find any?

  • Ross Presser

    Hi Brendan,

    I’ve been working with you quantization code, and I have hit a most confusing problem.

    On many images it works perfectly; but on this image, I am getting this exception:

    Unhandled Exception: System.ArgumentException: ‘-169′ is not a valid value for ‘red’. ‘red’ should be greater than or equal to 0 and

    less than or equal to 255.

    at System.Drawing.Color.CheckByte(Int32 value, String name)

    at System.Drawing.Color.FromArgb(Int32 alpha, Int32 red, Int32 green, Int32 blue)

    at System.Drawing.Color.FromArgb(Int32 red, Int32 green, Int32 blue)

    at ImageQuantization.OctreeNode.ConstructPalette(ArrayList palette, Int32& paletteIndex) in C:\Documents and Settings\btompkins\Desktop\ImageQuantization\OctreeQuantizer.cs:line 454

    It’s almost as if there is so much red in this image that the _red member (which was declared as an int) went negative?

    I’ll try isolating and maybe fixing this, but I’m not good with C#… I’ll let you know what if anything I discover.

  • Ian Kevan

    Tnaks Brendan, I will continue my search.

  • Brendan Tompkins

    Hi Ian,

    I’m sorry to say that I haven’t delved into other quantization methods since I first worked with this code. You should be able to plug a different quantizer in, however, if you find the algorithm out there somewhere.

    Good luck.

    Brendan

  • Ian Kevan

    Hi Brendan,

    Thanks for the quantization code. I have the same problem as one of your other users in that when applying the Octree quantization to a captured windows XP form, because of the complex gradients in the title bar and toolbar, you end up with a very blocky looking graphic. Have you had any joy determining a way or applying error diffussion to the Octree quantization method?

  • Ry

    Quick and easy for me to use. Thanks for putting this out there for us!!!

  • James

    Brendan,

    Lovely little library – thanks very much for posting this.

    What is the difference between the PalleteQuantizer and the OctetQuantizer?

  • darrel

    THANK YOU! I spent the last couple of days searching for a way to fix the GIF saving problems. I finally stumbled across the Quantizer article on MSDN but that was a tad over my head to get working. That finally led your DLL which couldn’t have been easier!

    I’m rather amazed that the default GIF behaviour in .net is based on the windows pallet, but, then again, maybe it’s not that surprising. ;o)

    As for the person with the JPG question, you need to set the codec compression rate. Here’s what I’m using (VB.NET):

    ====================

    myImageCodecInfo = GetEncoderInfo("image/jpeg")

    Dim myEncoderParameters As EncoderParameters = New EncoderParameters(1)

    ‘ this line sets the JPG compression quality

    ‘(100 = best quality, 0 = most compression)

    Dim myEncoderParameter As EncoderParameter = New EncoderParameter(myEncoder, 90)

    myEncoderParameters.Param(0) = myEncoderParameter

    imgOutput.Save([your path], myImageCodecInfo, myEncoderParameters)

    imgOutput.Dispose()

    ========================

  • Avalon

    Briliant…. there i was trying to work out a formual to correct the darkening of Png images in IE when i came across this little wonder code :) Life saver, Still need to fixteh png problem sometime though, Also does you code work with png transparency too… i ahve yet to test it.

  • Andrew

    Great code, saved me from trying to do the same thing myself and wasting hours of time!

    Thanks!

  • NatoCM

    Brendan, thank you so much for your code. It worked just fine! Regards from Brazil!

  • Christoph Pecher

    The quantization algorithm is not enough. I have problems with areas slowly changing color(gradients). I found an article concerning that issue, but no code that i cound translate to c-sharp. Anyone knows about how to implement the Error Diffusion Dithing in the second pass of the octree quantization? http://www.leptonica.com/applications.html#COLOR-QUANTIZATION

  • Jerry Spaeder

    I downloaded the DLL you created for Cheri Verdend. Works great!

    Thanks,

    Jerry

  • Linus Betschart

    Hello

    is it possible to save BMP Bitmaps with this class in 16 to 256 colours? With .net itself it’s as I know not possible to save in different colour formats…

    thx

    LB

  • Brendan Tompkins

    Glad to have helped Daniel. Good luck!

    B

  • Daniel

    This class worked perfectly for me. I have a few classes that I always keep nearby because they are so damn good: ImageQuantizer is now one of them.

    Thank you thank you thank you thank you …

  • Brendan Tompkins

    Herb… Glad to have helped!

    Brendan

  • Herb

    Brendan – Great work. Just came across your article while trying to fix this problem in outputting gifs, downloaded your dll, and five minutes later my gifs look beautiful. Thanks so much for your contribution.

  • Brendan Tompkins

    Well, I haven’t poked around in the code in a while, but I think it’s just a matter of overloading a method to pass in the bit depth you are looking for…

  • Tommaso

    eS: 16 color…

  • Tommaso

    And if i want a optimized color reduced gif ?

  • Fred Thieme

    Brendon:

    Here is a method to set the transparent color for the gif file. First your dll optimizes the palette. Then before saving the file call the SetTransparentColor method on the optimized palette. The .NET Gif encoder will use the slightly modified palette to actually set the transparent color in the file. None of the color values are actually changed just the Alpha values. Check it out — it works.

    Private Sub ConvertToGif()

    ”load image to convert

    Dim BMPQuantized As Bitmap

    Dim BMPSource As Drawing.Image = Image.FromFile("C:\Ant.bmp")

    ”optimize the palette

    Dim quantizer As New OctreeQuantizer(255, 8)

    BMPQuantized = quantizer.Quantize(BMPSource)

    ”set the transpatent color where Color.Black is the desired transparent color

    BMPQuantized.Palette = SetTransparentColor(BMPQuantized.Palette, 255, Color.Black)

    ”save as new .gif

    BMPQuantized.Save("C:\AntConverted.gif", System.Drawing.Imaging.ImageFormat.Gif)

    MsgBox("end")

    End Sub

    Private Function SetTransparentColor(ByRef SrcPal As ColorPalette, ByVal nColors As Integer, ByVal cT As Drawing.Color) As ColorPalette

    ”set the trans color in SrcPal

    Dim i As Integer

    Dim C As Drawing.Color

    Dim Clrs() As Drawing.Color = SrcPal.Entries

    For i = 0 To nColors

    C = Clrs(i)

    If (C.R = cT.R) And (C.G = cT.G) And (C.B = cT.B) Then ”match

    SrcPal.Entries(i) = Color.FromArgb(0, C.R, C.G, C.B) ”trans

    Else

    SrcPal.Entries(i) = Color.FromArgb(255, C.R, C.G, C.B) ”opaic

    End If

    Next i

    Return SrcPal

    End Function

  • Fred Thieme

    Great — thanks for responding. I will plug it in and see how it goes.

  • Brendan Tompkins

    Fred,

    What’s out there is the latest. I’m not aware of any glaring bugs. I have the dll in production on a server and it seems to be chugging along.

    -B

  • Fred Thieme

    Brendan

    Thanks for the very useful color reduction code/dll. I came across your work while doing research for a pet project of mine. I was just gearing to do what you have already done I even read the same MSDN article that you site in your intro. I had your dll working in a VB test project in about 5 min. One Question — Have you made any improvements or fixed any Bugs since posting the code and dll that I downloaded today 4/8/2004. Thanks again.

    Fred

  • Groves

    Just wanted to thank you for your hard work. That library works like a charm — I was having trouble with the standard ImageFormat.GIF exporting transparent, but yours works great.

    Oh, and to Gary who’d asked about the transparency. If you haven’t found it yet, try <b>MyImage.MakeTransparent(Color.White)</b>

    Thanks, again,

    Groves

  • Brendan Tompkins

    Right. JPEG images aren’t quantized, because each pixel’s color value is mathematically determined, not taken from a palette. There’s got to be a way to set the level of compression, which relates to image quality. Not sure how to do this, however…

  • Homam

    Thanks! It’s very useful.

    Is there any way for quantization Jpeg images? I mean how can i save a jpeg image in a differrent quality?

  • Gary Peluso

    Is there a way to set the transparent color? It defaults to black, but I need it to be white.

    Thanks

  • Cheri Verdend

    Brendan,

    Ah! I finally got it working. Something was wrong with my .NET installation. Even though the dll was in the bin directory, I continued to get errors that that the namespace could not be found. After a myriad of false starts I finally uninstalled and reinstalled .NET – and now it works perfectly!

    Thank you so much for your help!

  • Brendan Tompkins

    As to where to add this code… Try replacing this line of code from your original source with the above code:

    objBitmap.Save(server.mappath("Image.gif"), System.Drawing.Imaging.ImageFormat.Gif)

  • Cheri Verdend (yes, I mistyped my name before)

    Hi Brendan,

    Thank you for responding so quickly! I’m trying to do what you told me to do… but I’m still a bit lost. (This is my first experience with .cs files, and I’m doing this in a text editor, not VS).

    I’ve created an IIS virtual directory, in which I have a directory named "bin" (I hope that is all I need to do to create a bin directoy). In the bin directory I have saved the "ImageQuantization.dll" and the "ImageQuantization.pdb" files. I also saved my aspx file and all the files from your ImageQuantization.zip into the root of my virtual directory.

    Below is the code for my aspx file. Right now it creates and saves a GIF with ugly colors. Would you please tell me exactly what code to insert and where to insert it to: add a project reference to the DLL and to create an instance of the PaletteQuantizer object.

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    aspx code starts here

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    <%@ Page Language="VB" Debug="true" %>

    <%@ Import Namespace="System.Drawing"%>

    <%@ Import Namespace="System.Drawing.Text"%>

    <%@ Import Namespace="System.Drawing.Imaging"%>

    <%@ Import Namespace="System.Drawing.Drawing2D"%>

    <script language="VB" runat="server">

    Sub Page_Load(sender As Object, e As EventArgs)

    If Request("StringToDisplay")<>"" then

    ‘Create the bitmap

    Response.Clear( )

    Dim objBitmap As New Bitmap(180,30, pixelformat.Format32bppArgb)

    Dim objGraphic as Graphics = Graphics.FromImage(objBitmap)

    objGraphic.Clear(Color.FromArgb(0, 255, 255, 255))

    objGraphic.SmoothingMode = SmoothingMode.AntiAlias

    objGraphic.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias

    Dim textBrush As New SolidBrush(Color.FromArgb(255, 51, 0, 102))

    Dim bgBrush As New SolidBrush(Color.white)

    objGraphic.FillRectangle(bgBrush, 0, 0, 180, 30)

    Dim objPoint As New PointF(0,0)

    Dim objFont As New Font("Arial", 11, FontStyle.Bold)

    objGraphic.DrawString(Request("StringToDisplay"), objFont, textBrush, objPoint)

    ‘Save the image to file

    objBitmap.Save(server.mappath("Image.gif"), System.Drawing.Imaging.ImageFormat.Gif)

    objGraphic.Dispose( )

    objBitmap.Dispose( )

    End if

    End Sub

    </script>

    <html><head><title>Text creation test</title></head><body><BR><BR>

    <form method="POST" action="test.aspx">

    String to write: <input type="text" name="StringToDisplay">

    <input type="submit" value="Submit Text">

    </form>

    </BODY></html>

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    After several days of searching and searching for a solution, I feel like I’m very close. You are a life-saver. Thank you, again, for your help!

  • Brendan Tompkins

    Sorry Chrei, you need to create an instance of the PaletteQuantizer object

  • Brendan Tompkins

    Chrei… No problem. You can certainly use my assembly from VB.NET. That’s the beauty of .NET IL! I built the dll for you, here’s the download, with the PDB debug info too:

    http://www.intrinsigo.com/bsblog/ImageQuantizationDll.zip

    Drop this dll into your site’s bin directory, and add a project reference to the DLL. You should then be able to create an instance of the ImageQuantizer from your aspx vb code, and call its Quantize method.

    -Brendan

  • Chrei Verdend

    Please be patient – this is all new to me.

    I am trying to create an aspx page, written in VB, that receives text from a form’s input box, then generates a gif of the text and saves it to file. Everything is working fine except that the default halftone palette is terrible. I understand that the GIF needs to be color quantized to make it look decent and I believe that is exactly what your code does.

    Problem is, I downloaded your project and have absolutely NO IDEA how to implement it into my apsx page. Your instructions say to "change the namespace and you’ve got your own adaptive image quantizer." Can you please explain how to do that, or point me in the right direction?

    Also, is it going to be a problem that my aspx page is written in VB? If so, do I need to convert my code to C# to run your script? (I have no idea how to do that, but I could try.)

    Otherwise, do you have any other suggestions for how I might get this accomplished?

    Thanks for any and all help!!

  • Brendan Tompkins

    Not sure Paul. Are you opening my sln file directly?

  • Paul Calderon

    I have problems with your code, with the dependencies of sistem.drawing

    Can you help me please