Most printing and displaying image devices cannot reproduce the real colors of an image due to their limited palettes. Consequently, the number of image colors has to be reduced to match the number of palette colors available. The method of color reduction is very simple: every color of the original picture is replaced by an appropriate color from the limited palette that is accessible. Unfortunately, the resulting image can become discolored and less attractive. Notice the difference between the original color image and the 32 color palette-based image:
Original true color image | 32 color palette-based
image without dithering |
There are several algorithms which allow you to save images in a more attractive format.
NeuQuant is a palette generation algorithm. It creates an optimal palette for a given image, what allows significantly reducing image size.
The following image was generated using the HeuQuant algorithm. Images size is 63% of the original image size.
NeuQuant palette-based image |
The really interesting feature of NeuQuant is that it preserves full alpha transparency. So, it allows converting real-color images with transparency to indexed PNG images almost without quality reduction. For example, the following image is reduced by 71%:
Original true color image | NeuQuant palette-based image |
To use NeuQuant you should set a ColorPaletteType.NeuQuant palette as a ColorManagementProvider.Palette. The following snippet demonstrates how to use the NeuQuant algorithm:
using (var bitmap = new Bitmap(@"Images\in.png")) { bitmap.ColorManagement.Palette = new ColorPalette(ColorPaletteType.NeuQuant); bitmap.ColorManagement.Convert(PixelFormat.Format8bppIndexed); bitmap.Save(@"Images\Output\out.png"); }
Dithering is an algorithm class which allows the creation of color depth illusions in images with a limited color palette. The main idea of these algorithms is the approximation of image colors which are not available in the palette, by organizing pixels in a special way. Dithering introduces patterns into an image which are perceived by the eyes as new colors, but these are colors which are not available in the palette.
One of the dithering parameters is intensity - the ratio between the images with and without dithering. When 0% intensity is used, no dithering is enabled.
There are 3 main algorithms in the dithering class:
The noise dithering algorithm builds patterns randomly, adding a random number in the range [-1; 1] to each pixel of the original image. The following picture illustrates the result of noise dithering while creating a 32 color image:
Noise dithering |
This dithering algorithm builds dithering patterns in a very specific way. For simplicity let's imagine that the color palette has only two colors. The human eye tends to average a region around a pixel instead of treating each pixel individually, thus it is possible to create the illusion of many gray levels in a binary image, even if it has only two levels. With 2x2 binary pixel patterns, we can represent 5 shades of gray.
The same method is used when the count of colors in the palette is more than two. The patterns in the ordered dithering algorithm are predefined. The following pictures illustrate the result of ordered dithering with Bayer and Spiral patterns while creating a 32 color image.
Bayer ordered dithering | Spiral ordered dithering |
The error diffusion algorithm builds dithering patterns dynamically based on each pixel. After conversion the pixels of an original image can differ from the same pixels in an output palette-based image. This difference is called a "quantization error". Error diffusion achieves the effect of color emulation by distributing the quantization error to neighboring pixels that have not been processed yet. The following error diffusion filters are used in practice: Floyd-Steinberg, Fan, Burkes, Stucki, Sierra, Jarvice, and Stephenson. These filters use the preset coefficients of error diffusion. The following examples illustrate different diffusion filters.
Original dithering | Floyd-Steinberg
dithering |
Fan dithering |
Jarvis dithering | Stucki dithering | Sierra dithering |
Burkes dithering | Stephenson dithering |
All dithering algorithms have advantages and disadvantages. Noise dithering works very quickly but produces noisy images. Ordered dithering approximates color blends using fixed patterns; as a result, solid colors are emphasized and edges appear harder. Error diffusion scatters pixels irregularly, making edges and colors softer, but it takes much more time than the other algorithms.
Graphics Mill supports all the dithering techniques above while changing pixel format.
There are two ways to convert pixels to an indexed pixel format:
Here is a simple example which converts an image to an 8-bit indexed image with default dithering parameters:
using (var bitmap = new Bitmap(@"Images\DitheringMain.jpg")) { bitmap.ColorManagement.Dithering = DitheringType.None; bitmap.ColorManagement.Palette = ColorPalette.Create(bitmap, 32); bitmap.ColorManagement.Convert(PixelFormat.Format8bppIndexed); bitmap.Save(@"Images\Output\out.jpg"); }
The code below demonstrates how to modify all dithering settings. All the images on this page where generated with it.
using (var bitmap = new Bitmap(@"Images\DitheringMain.jpg")) using (var colorConverter = new ColorConverter()) { colorConverter.Palette = ColorPalette.Create(bitmap, 32); colorConverter.DestinationPixelFormat = PixelFormat.Format8bppIndexed; colorConverter.Opacity = 1; //Without dithering colorConverter.Dithering = DitheringType.None; using (var newbitmap = colorConverter.Apply(bitmap)) newbitmap.Save(@"Images\Output\Dithering_None_x_x_x_32.png"); //White noise colorConverter.Dithering = DitheringType.Noise; colorConverter.DitheringIntensity = 0.5f; using (var newbitmap = colorConverter.Apply(bitmap)) newbitmap.Save(@"Images\Output\Dithering_Noise_50_x_x_32.png"); //Ordered dithering colorConverter.DitheringIntensity = 1; colorConverter.Dithering = DitheringType.OrderedBayer2; using (var newbitmap = colorConverter.Apply(bitmap)) newbitmap.Save(@"Images\Output\Dithering_Ordered_100_Bayer_2_32.png"); colorConverter.Dithering = DitheringType.OrderedBayer4; using (var newbitmap = colorConverter.Apply(bitmap)) newbitmap.Save(@"Images\Output\Dithering_Ordered_100_Bayer_4_32.png"); colorConverter.Dithering = DitheringType.OrderedSpiral4; colorConverter.DitheringIntensity = 0.1f; using (var newbitmap = colorConverter.Apply(bitmap)) newbitmap.Save(@"Images\Output\Dithering_Ordered_100_Spiral_4_32.png"); //Error diffusion colorConverter.Dithering = DitheringType.Original; colorConverter.DitheringIntensity = 0.5f; using (var newbitmap = colorConverter.Apply(bitmap)) newbitmap.Save(@"Images\Output\Dithering_Original_100_x_x_32.png"); colorConverter.Dithering = DitheringType.FloydSteinberg; using (var newbitmap = colorConverter.Apply(bitmap)) newbitmap.Save(@"Images\Output\Dithering_FloydSteinberg_100_x_x_32.png"); colorConverter.Dithering = DitheringType.Fan; using (var newbitmap = colorConverter.Apply(bitmap)) newbitmap.Save(@"Images\Output\Dithering_Fan_100_x_x_32.png"); colorConverter.Dithering = DitheringType.Jarvis; using (var newbitmap = colorConverter.Apply(bitmap)) newbitmap.Save(@"Images\Output\Dithering_Jarvis_100_x_x_32.png"); colorConverter.Dithering = DitheringType.Stucki; using (var newbitmap = colorConverter.Apply(bitmap)) newbitmap.Save(@"Images\Output\Dithering_Stucki_100_x_x_32.png"); colorConverter.Dithering = DitheringType.Sierra; using (var newbitmap = colorConverter.Apply(bitmap)) newbitmap.Save(@"Images\Output\Dithering_Sierra_100_x_x_32.png"); colorConverter.Dithering = DitheringType.Burkes; using (var newbitmap = colorConverter.Apply(bitmap)) newbitmap.Save(@"Images\Output\Dithering_Burkes_100_x_x_32.png"); colorConverter.Dithering = DitheringType.Stephenson; using (var newbitmap = colorConverter.Apply(bitmap)) newbitmap.Save(@"Images\Output\Dithering_Stephenson_100_x_x_32.png"); }