Quantization, Palettes, and Dithering
Quantization is the part of image processing where you stop thinking in terms of continuous color and start thinking in terms of a limited palette. Even if you never call Quantize() directly, it still matters because formats like GIF, indexed PNG, CUR, and ICO rely on the same ideas.
In ImageSharp, quantization matters both as an explicit processing step and as part of formats that write indexed or palette-constrained output.
Where Quantization Shows Up
Quantization is relevant in a few common places:
Quantize()when you want to reduce colors as part of a processing pipeline.GifEncoder, because GIF output is palette based.PngEncoderwhen you targetPngColorType.Palette.IcoEncoderandCurEncoderfor icon and cursor workflows.
Use quantization when you want smaller palette-based outputs, fixed-color branding palettes, retro or posterized looks, or more control over indexed formats.
Quantize as a Processing Step
The default Quantize() overload uses KnownQuantizers.Hexadecatree, which is a fast, good general-purpose adaptive quantizer.
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using Image image = Image.Load("input.png");
image.Mutate(x => x.Quantize(new WuQuantizer(new QuantizerOptions
{
MaxColors = 64
})));
image.Save("output.png");
This remaps the image content to a smaller palette before you save it. That can be useful when you want the palette reduction to be part of the visible processing result rather than only an encoder detail.
Choose the Quantizer
KnownQuantizers exposes reusable built-in choices:
KnownQuantizers.Hexadecatreefor a fast adaptive quantizer with solid general results.KnownQuantizers.Wufor high-quality adaptive palette generation.KnownQuantizers.WebSafefor the fixed web-safe palette.KnownQuantizers.Wernerfor the fixed Werner palette.
When you need more control, create a quantizer directly:
WuQuantizerfor adaptive palette generation with configurableQuantizerOptions.HexadecatreeQuantizerfor fast adaptive quantization.PaletteQuantizerwhen you want to force output to a known palette.
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using Image image = Image.Load("input.png");
Color[] brandPalette =
{
Color.Black,
Color.White,
Color.ParseHex("0057B8"),
Color.ParseHex("FFD100")
};
image.Mutate(x => x.Quantize(new PaletteQuantizer(brandPalette)));
Dithering Choices
QuantizerOptions controls the main quantization tradeoffs:
MaxColorslimits the palette size.Ditherselects the dithering algorithm.DitherScaleadjusts how strongly dithering is applied.ColorMatchingModechooses how pixels are matched back to palette entries after the palette has been built.TransparencyThresholdandTransparentColorModeaffect how transparent pixels are reduced into the palette.
ColorMatchingMode has two built-in choices:
Coarseis the default and favors speed.Exactuses more precise palette matching for cases where the extra accuracy matters more than throughput.
The API surface is intentionally small here: pick the quantizer that builds the palette you want, then choose either Coarse or Exact for the palette-matching pass.
KnownDitherings exposes the built-in dithering algorithms, including ordered Bayer variants and error-diffusion algorithms such as Floyd-Steinberg, Atkinson, Burks, Jarvis-Judice-Ninke, and Stucki.
Set Dither = null when you want flatter output with no dithering pattern. Keep dithering enabled when you want to hide banding in gradients or other smooth transitions.
ImageSharp also has a separate Dither() processing extension. Its default overload reduces the image to the web-safe palette using KnownDitherings.Bayer8x8, and other overloads let you dither against a palette you provide.
Encoder-Time Quantization
Many palette-sensitive exports are better controlled at save time by configuring the encoder directly:
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
using Image image = Image.Load("input.png");
image.Save("output-indexed.png", new PngEncoder
{
ColorType = PngColorType.Palette,
Quantizer = new WuQuantizer(new QuantizerOptions
{
MaxColors = 128,
ColorMatchingMode = ColorMatchingMode.Exact,
Dither = KnownDitherings.FloydSteinberg,
DitherScale = 0.75F,
TransparentColorMode = TransparentColorMode.Preserve
}),
PixelSamplingStrategy = new ExtensivePixelSamplingStrategy()
});
This approach is usually the right choice when you want format-specific palette output without permanently changing the in-memory image first.
Sampling and Transparency
Encoders that implement quantizing behavior also expose a pixel-sampling strategy. The default strategy samples a subset of pixels on large inputs to keep palette generation practical. ExtensivePixelSamplingStrategy scans all pixels instead, which can improve results when rare colors matter, at the cost of more work.
Transparency handling matters most for GIF, palette PNG, ICO, and CUR output. TransparentColorMode controls how transparency is represented in the reduced palette, while TransparencyThreshold controls when partially transparent pixels are treated as transparent during quantization.
Related Topics
Practical Guidance
- Choose quantization when palette output is a requirement, not as a default quality improvement.
- Pick dithering based on the content; it can hide banding but add visible texture.
- Use extensive sampling only when rare colors matter enough to justify the extra work.
- Pay special attention to transparency for GIF, palette PNG, ICO, and CUR output.