If you’ve ever used an image compressor and set the color count to “Auto” for a PNG or GIF, you might wonder what’s actually happening under the hood.
Pick too few colors, and your smooth gradients turn into ugly, banded staircases. Pick too many, and you’re shipping hundreds of invisible palette entries that bloat your file size for no good reason.
┌────────────────────────────────────────────────────────────────────────┐
│ THE "AUTO COLOR" DILEMMA │
├───────────────────────────────────┬────────────────────────────────────┤
│ Too Few Colors (e.g., 16-32) │ Too Many Colors (e.g., 256) │
├───────────────────────────────────┼────────────────────────────────────┤
│ ❌ Visible color banding │ ❌ Wasted bandwidth & bytes │
│ ❌ Harsh, noisy dithering │ ❌ Unnecessary palette overhead │
│ ❌ Distorted anti-aliased text │ ❌ Negligible visual gain │
└───────────────────────────────────┴────────────────────────────────────┘
The fundamental problem is that there is no magic number. A simple 12-color flat icon needs a completely different treatment than a complex UI screenshot with subtle dropshadows.
To solve this, our compression pipeline uses an SSIM-bounded binary search. Instead of guessing a fixed palette size, the algorithm iteratively tests candidate palettes, checks them against a human visual perception model, and homes in on the smallest color palette that is visually indistinguishable from the original.
Visual Comparison: Before & After Quantization
Here is a side-by-side breakdown of a test image (~5,400 original colors) before and after running through our SSIM-bounded quantization pipeline.
+---------------------------------------+---------------------------------------+
| BEFORE: Original PNG | AFTER: SSIM-Quantized (116 Colors)|
| | |
| [ Image: Detailed UI Screenshot ] | [ Image: Quantized UI Screenshot ] |
| | |
| • Unique Colors: ~5,400 | • Unique Colors: 116 |
| • File Size: 412 KB | • File Size: 138 KB (-66.5%) |
| • Perceptual Loss: Baseline | • SSIM Score: 0.99947 |
| • Quality: Bit-perfect | • Visual Difference: Imperceptible |
+---------------------------------------+---------------------------------------+
Side-by-Side 400% Zoom Inspection
[ 400% Zoom: Sky Gradient Corner ] [ 400% Zoom: Sky Gradient Corner ]
Original (~5,400 Colors) Quantized (116 Colors - SSIM 0.99947)
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ Smooth tone transitions │ │ Smooth tone transitions │
│ No visible stepped bands │ VS │ Zero visible stepped bands │
│ Perfect alpha blend edges │ │ Intact anti-aliased text │
└──────────────────────────────┘ └──────────────────────────────┘
Result: Indistinguishable to human eyes at 1:1 and zoomed viewing distances.
How It Works in Practice: An Iteration Trace
Let’s walk through an actual run on a sample graphic containing around 5,400 unique colors. Our target quality threshold is MS-SSIM $\ge$ 0.9985.
| Step | Low Bound | High Bound | Midpoint Tested | MS-SSIM Score | Result & Decision |
| 1 | 32 | 256 | 144 | 0.99962 | PASS $\rightarrow$ high = 144 |
| 2 | 32 | 144 | 88 | 0.99918 | FAIL $\rightarrow$ low = 88 |
| 3 | 88 | 144 | 116 | 0.99947 | PASS $\rightarrow$ high = 116 |
| 4 | 88 | 116 | 102 | 0.99932 | FAIL $\rightarrow$ low = 102 |
| 5 | 102 | 116 | 109 | 0.99940 | FAIL $\rightarrow$ low = 109 |
| 6 | 109 | 116 | 112 | 0.99943 | Exit condition reached ($high – low \le 4$) |
- Final Result:116 colors
- Final Quality:SSIM 0.99947 relative to original
- File Reduction: Reduced color count by ~97.8% while staying well above our safety target.
In just 6 iterations, the algorithm slashed the palette down from 256 to 116 colors, saving significant payload without single visual artifact.
Why MS-SSIM? (Measuring What Humans Actually See)
Traditional pixel metrics like Mean Squared Error (MSE) or Peak Signal-to-Noise Ratio (PSNR) only measure raw numeric pixel differences. They don’t care how the human eye processes light, contrast, or spatial frequency.
The Structural Similarity Index Measure (SSIM) evaluates structural layout, luminance, and contrast variations:
SSIM Score Perceptual Impact
──────────── ───────────────────────────────────────────────────────
1.0000 Bit-identical original.
≥ 0.9985 Imperceptible difference under any normal viewing condition.
~0.9700 Subtle artifacts visible if you squint or compare side-by-side.
< 0.9500 Obvious banding, loss of detail, and color distortion.
Multi-Scale SSIM (MS-SSIM) & Worst-Block Metrics
Single-scale SSIM only looks at the image at native resolution. Multi-Scale SSIM (MS-SSIM) downsamples the image across 5 distinct scales ($1\times, 2\times, 4\times, 8\times, 16\times$) and evaluates perceptual quality across frequencies.
We apply the standard empirical weight vector across those scales:
$$\text{Weights} = [0.0448,\, 0.2856,\, 0.3001,\, 0.2363,\, 0.1333]$$
Notice how mid-range frequencies carry the highest weights ($0.3001$ and $0.2856$). That’s because human vision is most sensitive to mid-scale structural details.
[Scale 1: Full Res] ─── Weight: 0.0448 (Fine details)
[Scale 2: 1/2 Res] ─── Weight: 0.2856 (High contrast edges)
[Scale 3: 1/4 Res] ─── Weight: 0.3001 ★ Highest sensitivity
[Scale 4: 1/8 Res] ─── Weight: 0.2363 (Broad shapes)
[Scale 5: 1/16 Res] ─── Weight: 0.1333 (Global tone)
The “Worst-Block” Safety Check
Most image tools average SSIM scores across the entire canvas. This is a trap. If an image has a massive flat background and a small gradient sky in the top corner, a severe quantization flaw in the sky can easily get hidden inside a high average score.
To prevent this, we divide the canvas into $8 \times 8$ blocks with a stride of 2 and evaluate the worst-performing block in the image:
$$\text{Final Score} = \min_{b \in \text{blocks}} \text{MS-SSIM}(b)$$
If a single $8 \times 8$ block degrades below $0.9985$, the candidate palette fails automatically.
The Binary Search Engine Algorithm
Here is the simplified logic running inside our optimization loop:
Python
def find_optimal_palette(image, min_colors=32, target_ssim=0.9985):
low = min_colors
actual_colors = get_unique_color_count(image)
high = min(256, actual_colors)
optimal = high
# Step 1: Ensure quantization is viable at maximum palette size
max_quantized = quantize(image, colors=high)
if compute_ms_ssim(image, max_quantized) < target_ssim:
# Image is too complex for palette quantization (e.g. truecolor photo)
return actual_colors
# Step 2: Binary Search Loop
while (high - low) > 4:
mid = (low + high) // 2
candidate = quantize(image, colors=mid)
score = compute_ms_ssim(image, candidate)
if score >= target_ssim:
optimal = mid
high = mid # Try searching for an even smaller color count
else:
low = mid # Palette was too aggressive; increase color count
# Step 3: Fine-grained linear refinement
for count in range(optimal, max(low, optimal - 6), -2):
candidate = quantize(image, colors=count)
if compute_ms_ssim(image, candidate) >= target_ssim:
optimal = count
else:
break
return optimal
Why We Hard-Floor at 32 Colors
When you reduce an image below 32 colors, standard color quantizers rely heavily on error-diffusion dithering. Even if mathematical SSIM metrics remain artificially elevated, human eyes quickly notice noisy, halftone-like checkerboard patterns. We set a hard floor at 32 colors in auto mode to guarantee crisp, clean output.
(Users needing retro 8-color or 16-color aesthetic exports can still set those manually).
What Happens with Photographic Images?
If an input PNG is a full-color photo rather than an illustration or screenshot, even a full 256-color palette won’t pass the 0.9985 threshold.
When the search detects that maxQuantized fails right at step 1, it halts immediately, preserves the full color count, and flags the file for lossy WebP or JPEG conversion instead.
Why Adaptive Optimization Beats Fixed Rules
FIXED 64-COLOR DEFAULT
┌─────────────────────────────────────────────────────────────────┐
│ Simple Icon (14 Colors) ──► Quantized to 64 ──► Wasted Palette│
│ Complex UI (300 Colors) ──► Quantized to 64 ──► Banding Error │
└─────────────────────────────────────────────────────────────────┘
SSIM-BOUNDED ADAPTIVE SEARCH
┌─────────────────────────────────────────────────────────────────┐
│ Simple Icon (14 Colors) ──► Detects 14 ──► 0% Quality Loss│
│ Complex UI (300 Colors) ──► Finds 184 ──► Perfect Detail │
└─────────────────────────────────────────────────────────────────┘
Fixed defaults force every image into an arbitrary mold. Per-image SSIM-bounded search adapts directly to the content:
- A minimalist SVG export with 18 flat colors gets compressed to exactly 18 entries.
- A detailed dashboard mock-up with subtle anti-aliasing gets assigned 184 entries.
Every image receives the smallest possible palette needed to remain visually flawless.

Leave a Reply