System designExplainer

Color quantization for machine vision: cut pixel data, keep classes

Color quantization in machine vision: how median cut and k-means palettes shrink RGB frames to 1 byte or half a byte per pixel, and when two classes merge.

LumaSpec Vision11 min read Share on LinkedIn

On this page 9 sections
  1. 01What color quantization actually does
  2. 02Median cut vs k-means: how the palette gets built
  3. 03Where the saving lands (and where it does not)
  4. 04Sizing the saving: 256 colors on a 4000 × 3000 frame at 30 fps
  5. 05When quantization destroys the classes
  6. 06Choosing the palette size: the decisions
  7. 07Checks before you commit
  8. 08Where to go next
  9. 09Questions

An 8-bit RGB frame carries 24 bits per pixel and 2^24 = 16,777,216 possible colors, yet most inspection tasks separate a handful of classes: pass or fail, wire brown or wire blue, label type A or type B. Color quantization maps every pixel to the nearest entry in a small palette, typically 16 to 256 colors, and stores the index instead of the raw triplet. That cuts frame memory by three to six times and speeds up any step that reads color per pixel. This guide explains how median cut and k-means build the palette, where the saving lands in a machine-vision pipeline, how quantization silently merges two classes you need to tell apart, and which palette size fits which job.

What color quantization actually does

An 8-bit RGB pixel holds three channels of 256 values each, and almost no vision task needs that many combinations. Quantization picks K representative colors — the palette — and replaces every pixel with the index of its nearest palette entry. Storage drops from 24 bits to 8 bits per pixel at K = 256, and to 4 bits at K = 16 when two indices share one byte. Nothing changes on the sensor or on the cable: quantization is a processing step applied after color interpolation and white balance, on the host PC or on the camera's own processor.

index(p) = argmin over k of ||p - c_k||^2 p = pixel RGB, c_k = palette entry k bits per pixel = ceil(log2 K) K = palette size, indices packed bytes per frame = width × height × bits per pixel / 8

The mapping is lossy: a pixel snaps to the nearest entry and the leftover difference is discarded. What you gain is a stable label. Classification and segmentation then run over K values instead of millions, and a precomputed 32 × 32 × 32 lookup table — 32,768 entries — turns assignment into a single fetch instead of up to K distance checks per pixel.

Where it shows up: sorting parts by body color, wire and label classification, and segmentation of textured organic surfaces. Published medical work quantizes photographs to segment skin lesions; the mechanics are the ones an industrial sorter uses to split good shells from stained ones.

Median cut vs k-means: how the palette gets built

Median cut starts from one box that holds every color in the image. It repeatedly takes the box holding the most pixels, splits it along its longest axis at the median pixel, and stops when K boxes exist; each box's average becomes a palette entry. The design goal is that every output color represents roughly the same number of input pixels, so entries land where the image actually has colors. It is one pass over the color histogram and deterministic: same image in, same palette out.

k-means starts from K seed colors, assigns every pixel to its nearest seed, moves each seed to the mean of its pixels, and repeats until assignments stop changing. It minimizes the summed squared distance between pixels and their entries, so at equal K it usually sits closer to the image than median cut. Published comparisons sum up the character difference: median-cut output keeps more fine image detail, k-means output gives more homogeneous regions. The price is speed — roughly an order of magnitude slower without implementation tricks — and the result follows the seed, so fix the seed or initialize from median cut to make a production recipe reproducible.

Region-first variants group pixels into superpixels and quantize the region colors with either algorithm, which runs faster on large frames at a small quality cost.

Median cut k-means
Split rule Fullest box, longest axis, at the median Seeds move to pixel means until stable
Data passes One histogram pass Iterative, several
Relative speed Fastest An order of magnitude slower without tricks
Output character More fine detail More homogeneous regions
Reproducibility Deterministic Follows the seed; fix it
Use as First palette, or final one for well-spaced classes Final palette when classes sit close

Where the saving lands (and where it does not)

The camera-to-host link never sees a palette. Standard cameras transmit raw Bayer or mono, one byte per pixel, before color interpolation — a 12 MP frame crosses the cable as 12 MB whichever palette the software runs. The saving lands after the camera: host RAM per frame, buffer counts, disk, and every per-pixel loop that reads color.

Inside a smart camera the whole pipeline runs on-device, so bytes per pixel trade directly against frames of buffer and loop time; the camera's output to the PLC is already just labels and coordinates. The LS-SMC-0063 pairs a 1.6 MP sensor with a 60 fps on-device loop. At one byte per pixel a frame is 1.6 MB, at three it is 4.8 MB — the difference between one frame of buffer and three.

Integrated lighting plus on-device classification is the natural home for a frozen palette:

Smart cameras & vision sensorsHikrobot MV-SC6050C-08M-XBN SC6000 Series Smart Camera, 5 MP, Color, 8 mm focal length, M12-mount, Multi-color light sourceMV-SC6050C-08M-XBN
Interface
GigE
Resolution
5 MP
Sensor format
1/1.45"
Shutter
Global
$3,449.00 · quotedView and quote

Five megapixels of RGB8 costs about 15 MB per frame; at one byte per pixel it is 5 MB — memory the camera spends on frames in flight instead.

The same arithmetic scales with the sensor, which is where camera choice enters:

Specification MV-CU020-90GC MV-CS200-10GC LS-CMR-0166
Sensor type CMOS, global shutter CMOS, rolling shutter –
Sensor model GMAX4002 Sony® IMX183 Sony IMX226
Pixel size 4 μm × 4 μm 2.4 μm × 2.4 μm 1.85 μm
Sensor size 1/1.7" 1" 1/1.7"
Resolution 2048 × 1200 5472 × 3648 4000 × 3000 (12MP)
Max. frame rate 49 fps @ 2048 × 1200 Bayer GB 8 5.9 fps @ 5472 × 3648 30 fps
Dynamic range 66.9 dB 65.5 dB –
SNR 40.8 dB 41.5 dB –
Gain 0 dB to 11.1 dB 0 dB to 24 dB –
Exposure time 4 μs to 10 sec 46 μs to 2.5 sec –
Exposure mode Off/Once/Continuous exposure mode Off/Once/Continuous exposure auto, supports Timed and Trigger Width exposure mode, and Rolling and Global Reset sensor shutter mode –
Pixel format Bayer GB 8/10/10Packed/12/12Packed Mono 8/10/12, Bayer GB 8/10/10p/12/12p, YUV422Packed, YUV422_YUYV_Packed, RGB 8, BGR 8 –
Price $309.00 $739.00 $560.00

Values as printed in each product's data sheet; open a part for its drawing and full specifications.

At RGB8 the frames cost about 7.5 MB, 60 MB and 36 MB each; quantized to 256 colors, about 2.5 MB, 20 MB and 12 MB.

When the link rather than the palette is the constraint, choose the interface for the wire number — USB3 machine vision camera selection: compare sensor trade-offs walks that decision. If a GigE camera arrives without color at all, that is the link's pixel-format default, not your palette: see GigE camera shows no color image: fix the pixel format default.

Sizing the saving: 256 colors on a 4000 × 3000 frame at 30 fps

Inputs, with their sources:

  • Resolution and rate: 4000 × 3000 pixels at 30 fps — the printed figures for the LS-CMR-0166, a 12 MP USB3 camera shown below.
  • Pixel format: RGB8 after color interpolation, 8 bits × 3 channels = 3 bytes per pixel.
  • Palette: K = 256 gives 8 bits = 1 byte per pixel; K = 16 gives 4 bits = 0.5 bytes per pixel, two indices packed per byte.
  • Units: 1 MB = 10^6 bytes.

Rule in words: frame size = pixels × bytes per pixel, and per-second traffic = frame size × frames per second.

Arithmetic:

  • Pixels: 4000 × 3000 = 12,000,000
  • RGB8 frame: 12,000,000 × 3 B = 36,000,000 B = 36 MB
  • 256-color frame: 12,000,000 × 1 B = 12,000,000 B = 12 MB — a third of RGB8, 24 MB saved per frame
  • 16-color frame packed: 12,000,000 × 0.5 B = 6,000,000 B = 6 MB — a sixth of RGB8
  • Traffic at 30 fps: 36 × 30 = 1,080 MB/s (RGB8), 12 × 30 = 360 MB/s (256 colors), 6 × 30 = 180 MB/s (16 colors)
  • Wire cross-check: raw Bayer leaves the camera as 12,000,000 × 1 B = 12 MB per frame, so 12 × 30 = 360 MB/s — above GigE's practical ~120 MB/s of payload, comfortable on USB3's practical ~400 MB/s. The palette never changes this wire figure.

Result: one byte per pixel cuts frame memory and host traffic to a third of RGB8; half a byte cuts both to a sixth.

Decision: run 256 colors as the default — the 3× cut carries no class risk while palette entries stay spaced. Drop to 16 packed only when classes number about eight or fewer and you have tested that every pair lands in its own palette cell; the loop then touches 6 MB per frame instead of 36.

The example's resolution and frame rate are this camera's printed figures:

Area scan cameras12 MP USB3 area scan camera, IMX226, 1/1.7" rolling shutter, 30 fps, 4000 × 3000 resolution (LS-CMR-0166)LS-CMR-0166
Sensor format
1/1.7"
Interface
USB3
Resolution
12 MP
Shutter
Rolling
$560.00View partBuild a system with it

When quantization destroys the classes

A palette is learned under a light. Shift the line's exposure, white balance or ambient lighting and pixels drift toward the wrong entries, so classes mislabel long before anything errors out. That is why color-class set-ups nail illumination first: ambient light variation in pick and place vision: control it covers the drift, and wire color classification in machine vision: five colors, one set-up treats fixed lighting as the precondition for five reliable classes.

Two classes closer together than a palette step collapse into one entry. Build the palette on sample images of every class, then check the indices: two classes sharing one index, or one class split across two, means raise K or rebuild in Lab, where distances track visible color difference better than raw RGB.

Re-fitting the palette per frame renumbers the indices — index 7 is blue on frame 1 and green on frame 2, and downstream logic reads last Tuesday's map. Freeze one palette per recipe and keep it for the life of the set-up.

Choosing the palette size: the decisions

Palette Bits per pixel Frame at 12 MP Fits when
16 colors, packed 4 (0.5 B) 6 MB Up to about eight classes, each pair tested into its own cell
256 colors 8 (1 B) 12 MB General classification; ten or more classes or graded shades
RGB8, no palette 24 (3 B) 36 MB Measurement, gradients, continuous-tone surfaces

Checks before you commit

  • Build the palette offline from a set that covers every class, accept and reject parts alike, at production lighting, exposure and white balance.
  • Freeze the palette into the recipe and log a hash of it, so an accidental rebuild surfaces at start-up.
  • Confirm the quantize step runs without dithering.
  • Label test: every class maps to exactly one distinct palette index across the sample set.
  • Error test: count pixels whose class differs between full-RGB nearest-center and the palette lookup; keep it at or near zero, and raise K if it climbs past a few percent.
  • Buffer arithmetic: frames in flight × quantized frame size — 12 MB at 256 colors for the 12 MP example — must fit the device's RAM.
  • Rate test at line speed: 360 MB/s of RGB8 at 30 fps is real host traffic, and it repeats every cycle.

Where to go next

Pick the camera before the palette, because quantization multiplies whatever the sensor delivers. The area scan cameras page lists what each part actually prints, the systems topic page gathers the rest of the pipeline guides, and the contact page reaches the applications team — expert support comes with every order.

Build the whole station

The configurator matches a camera, lens, light, light controller and cables to each other, step by step, and lists only parts that fit what you already picked.

Start with the camera

Questions

Does color quantization cut camera-to-PC bandwidth?

No. A standard area-scan camera transmits raw Bayer or mono, one byte per pixel, before any palette exists — the 12 MP frame from the worked example crosses the link as 12 MB regardless of what the software does later. Quantization runs after color interpolation, on the PC or inside a smart camera, so the win is memory and processing there. If the link itself is the bottleneck, the fix is the interface, a smaller ROI or a different camera.

How many colors should a classification palette have?

Start at 256: one byte per pixel, and enough entries that ten or more classes keep separate cells with margin. Drop to 16 — half a byte per pixel packed — only when you can list every class and sample images prove each pair lands in a different cell. If one class shows two dominant shades, give each shade its own entry; K can go to 256 while storage stays at one byte.

Should dithering be on when quantizing inspection images?

No. Dithering trades per-pixel accuracy for how smooth the image looks to a person: error diffusion swaps each pixel for a pattern of palette colors, so region averages and template scores go wrong with it. The histogram stays roughly correct while individual pixels drift, which is the wrong trade for classification and always wrong for measurement.

Is color quantization the same as posterization?

Related, not equal. Posterization usually reduces bits per channel independently: 4 bits per channel gives 4,096 fixed colors on a rigid grid, whether or not the image uses them. Quantization chooses the K representative colors that best fit the image's own histogram, so class colors land on entries that sit near them. At the same bit budget, that makes quantization the better fit for class work.

Can the palette be built in Lab instead of RGB?

Yes, and it often helps. CIELAB is perceptually uniform, so distance in Lab tracks visible color difference better than RGB distance, and k-means in Lab tends to split close classes more sensibly. Build after white balance, run palette construction and pixel assignment in the same space, and expect the conversion to add one color-space transform per image or per pixel to the budget.

How do palette indices stay stable across frames and shifts?

Build the palette once, offline, from a representative set captured at production lighting, exposure and white balance. Freeze it and ship it inside the inspection recipe; never re-fit per frame, because a re-fit renumbers indices and the downstream logic keeps reading the old map. Log a hash of the palette so an accidental rebuild is caught at start-up rather than at the reject bin.