Some defects are defined by what disappears. On a laminated pill card, a good surface carries a regular pattern of small dark dots; the defect is a patch where the dots are gone, while every remaining pixel sits at nearly the same gray value as its neighbors. A global gray threshold cannot see that, because the defect is neither bright nor dark. This guide shows you how to build a local variance map - a per-window measure of how widely gray values spread - flag the windows where texture has vanished, and reject on the flagged area. It gives the window-size rule, a worked pill-card example with the arithmetic written out, the imaging choices that keep the method stable between parts, and the checks to run before production.
Why gray level fails and texture carries the signal
A global threshold picks one gray cutoff for the whole image and separates classes that sit in different brightness bands; the Otsu method applied on the Lab color space is the standard example. A missing-dot patch shares its brightness band with the good background, so no single cutoff isolates it. Segmentation taxonomies treat this as a texture problem rather than a brightness problem, which the Wikipedia segmentation overview makes explicit.
Local adaptive thresholds move the cutoff per neighborhood, which fixes uneven shading: the local mean adaptive thresholding method and adaptive thresholding for uneven part shading both work when the object is brighter or darker than its surround. The missing-dot patch is not brighter or darker - it is smoother. The quantity that changes is the spread of gray values inside a small window.
Spread also explains part-to-part drift. Dye lots and lamp aging shift mean gray, and the brightness drift that forces every auto mode off rescales the whole histogram. A slow shift moves a window's mean and its values together and barely touches the spread, so a variance map survives drift that breaks gray cutoffs.
The variance map in two box filters
For each window of N pixels, m is the mean gray value and σ is the standard deviation in gray levels. Threshold σ, not σ²: gray levels are easier to reason about than squared units, and your σ_min then reads directly as missing contrast in gray levels.
Compute both terms with box filters (running sums), never with per-pixel loops. A naive 128 × 128 window over a 4000 × 3000 image costs about 4000 × 3000 × 16384 ≈ 2 × 10¹¹ operations per frame; two running sums cost about 2.4 × 10⁷, nearly four orders of magnitude less, and the cost stays flat no matter how wide the window grows. Most PC vision libraries expose the pieces as a mean or box filter applied to the image and to its square; the point, local and global preprocessing primer places such local filters in the wider toolkit.
Choosing the window size
Two bounds, both in pixels, bracket the window side w:
- Lower bound: at least one texture period, so a good window almost always contains a dot and its edges. One pitch is the minimum; two or three are safer.
- Upper bound: no wider than the smallest defect you must catch. A window wider than the defect always includes good texture around it, so σ never sinks below the threshold and the defect slips through.
In compact form: dot pitch ≤ w ≤ smallest defect width. Defects narrower than about one window never form a fully quiet core, so the window also sets the detection floor: anything below roughly one window across is invisible to the map. When the two bounds collide, shrink the pixel scale rather than compromise - a finer sensor resolves the same pitch with more pixels per millimeter and widens the gap between the bounds.
From map to reject decision
- Acquire with exposure and gain locked.
- Convert to single-channel gray; apply a 3 × 3 median if sensor noise shows, and calibrate after it, because the median shaves a little spread off good areas.
- Compute σ with the two box filters and the window chosen above.
- Flag windows with σ < σ_min. Calibrate σ_min from histograms: run about 20 good images including the worst deformation cases and note the lowest σ that must pass; run known defects and note the highest σ that must fail; set σ_min at or below half of the passing value.
- Clean the mask: a 3 × 3 open removes speckle, close and hole-fill consolidate the patch, and blobs of a few windows or less get dropped.
- Reject when the largest connected low-variance blob exceeds the area limit, and store an overlay image of every reject for audit. The blob region analysis workflow covers the area bookkeeping, and a live binary preview during commissioning shows each flag as it happens.
Worked example: a missing-dot patch on a laminated pill card
The part is a two-layer laminate whose good surface carries dark dots on a 0.75 mm pitch; the defect forms where lamination pressure flattens the dots over a patch. The numbers below are illustrative for this part class; the arithmetic and the decision rules are what transfer to your part. The camera is the LS-CMR-0166, a 12 MP USB3 model reading 4000 × 3000 px. The optics cover one card with a 30 mm × 22.5 mm field of view.
Pixel scale. 30 mm / 4000 px = 0.0075 mm/px. The pitch maps to 0.75 / 0.0075 = 100 px, and a 0.27 mm dot spans 0.27 / 0.0075 = 36 px.
Window. The narrowest defect to catch is 1.2 mm = 160 px. The bounds give 100 ≤ w ≤ 160, so w = 128 px, or 0.96 mm across, sits between them.
Signal model. A 0.27 mm dot on a 0.75 mm pitch covers π × 0.135² / 0.75² ≈ 10% of the window area. Model a good window as 10% of pixels on the dark dots at gray 40 and 90% on the film at gray 160.
Variance, good window. m = 0.1 × 40 + 0.9 × 160 = 148. mean(x²) = 0.1 × 40² + 0.9 × 160² = 160 + 23040 = 23200. σ² = 23200 - 148² = 23200 - 21904 = 1296, so σ = 36 gray levels.
| Window content | Dot share | σ (gray levels) | Flagged at σ_min = 10? |
|---|---|---|---|
| Good pattern | 10% | 36 | no |
| Deformation dip | 3% | 20.5 | no |
| Missing dots | 0% | 0 | yes |
The dip row is the lowest good signal: with only 3% of dots surviving, m = 156.4, mean(x²) = 0.03 × 1600 + 0.97 × 25600 = 24880, σ² = 24880 - 156.4² = 419, and σ ≈ 20.5. The rule sets σ_min at or below half of that, so σ_min = 10 gray levels keeps dips out with (20.5 - 10) / 20.5 ≈ 51% margin, while every dot-free window at σ ≈ 0 flags.
Area and decision. The target defect spans 1.8 × 1.2 mm, which maps to 1.8 / 0.0075 = 240 px by 1.2 / 0.0075 = 160 px, a blob of 240 × 160 = 38400 px. In square millimeters: 38400 × 0.0075² = 2.16 mm². Checks on known-good parts found deformation dips up to 0.8 mm², so the area limit goes to 1.5 mm²: 1.5 / 0.8 ≈ 1.9× the worst good part, and still below the 2.16 mm² defect. Expressed in pixels, the same limit is 1.5 / 0.0075² ≈ 26667 px.
Decision: reject the card when the largest connected region with σ < 10 exceeds 1.5 mm² (≈ 26667 px). The 2.16 mm² patch rejects, the 0.8 mm² dip passes with a 1.9× margin, and any patch below the 0.96 mm window floor is out of this map's reach until you refine the pixel scale.
Imaging choices that keep variance stable
Compute the map on gray values from a mono sensor; demosaicing on color sensors averages channels and smooths fine texture, so on a color camera convert to luma first. Light the laminate from behind through a diffused source: dots must contrast against the film and the background must stay even, because a hot spot or a shadow adds spread that is not texture. For a card web, a long diffused backlight does this; the LS-BAR-0295 is a 1272 × 22 mm diffused bar light on 24 V, made for exactly this kind of strip.
- Size
- 400 mm +
- Type
- Diffused
- Voltage
- 24 V
Glossy film adds specular sheen, so put a polarizer on the light and an analyzer on the lens; the pill lighting guide for stain and edge-shadow control applies the same logic to shiny tablets. For a self-contained station, the LS-SMC-0063 carries a 1.6 MP sensor, a 16 mm lens and a white light with polarized and transparent-light modes, streaming over GigE at 60 fps - workable when the dots are coarse enough for that fixed lens.
- Interface
- GigE
- Resolution
- Up to 3 MP
Resolution needs are modest: keep each dot at least 5 px across its diameter, below which dot edges smear into the film and good-area σ collapses toward the defect level. At this example's scale the 0.27 mm dot has 36 px, so a 4000 px sensor could serve a field of view up to about 0.27 / 5 ≈ 54 mm before dots get marginal. The calculator below converts your dot size and field of view into the pixel count you need.
On a moving web, hold motion blur under 1 px by keeping exposure ≤ pixel scale / web speed: at 0.0075 mm/px and 150 mm/s that means 50 µs or less, and at a coarser 0.05 mm/px the limit relaxes to about 330 µs. Continuous motion also favors a global-shutter model from the area scan cameras category, so exposure and readout do not fight each other.
For indexed cards presented to a stationary camera, the LS-CMR-0166 fits the task: 4000 × 3000 px over USB3 with a rolling shutter.
- Sensor format
- 1/1.7"
- Interface
- USB3
- Resolution
- 12 MP
- Shutter
- Rolling
Where the method fails
- Brightness-only defects. Stains and discoloration change gray level, not spread; the variance map stays quiet, and those need a brightness or color route instead.
- Inverted texture defects. Extra dots, raised specks and scratches push σ up rather than down. The same map catches them with a second threshold, σ > σ_max; the scratch detection workflow with dynamic thresholding shows that direction of analysis.
- Sub-window defects. Anything narrower than the window leaves σ above threshold. In the worked example the floor is the 0.96 mm window; smaller patches need a finer pixel scale, not a bigger threshold.
- Directional patterns. Variance ignores orientation, so a defect that keeps the contrast but rotates the pattern passes. Use directional filters or gradient histograms for those.
- Whole-part pattern loss. If forming thins the pattern everywhere, good-area σ itself shifts and the calibration must be redone per part family. When candidate settings look close on hard images, the NIST segmentation difficulty note provides a scalar difficulty score for comparing them.
Checks before production
- Run at least 50 known-good parts, including the worst deformation cases: the reject count must be zero. Record the largest blob seen - 0.8 mm² in the example.
- Run a defect library at descending severity and record the smallest caught defect; confirm it clears the window floor.
- Keep the σ histogram per shift: a slow fall in good-area σ flags lamp aging before it turns into missed defects.
- Recalibrate σ_min and the area limit the same day you change any lamp, lens or exposure, because gray values rescale.
- Audit every reject from its stored overlay image; a patch flagged where the dots are visibly present means the window or σ_min is wrong, not the part.
If you are speccing hardware for this inspection, start in the camera finder or the light finder, or work through the camera configurator; the smart cameras category covers the self-contained route and bar lights the backlight options. Related inspection methods live on the applications topic page, and for a second opinion on a difficult part you can reach the team through the contact page.
Questions
What window size should I start with for a local variance filter?
Set the window side between the texture pitch and the smallest defect you must catch, both measured in pixels. One pitch is the minimum; two to three are safer. Measure the pitch on a good image and the narrowest defect on a known bad one, then pick in between. With a 100 px pitch and a 160 px smallest defect, a 128 px window fits both bounds.
Why does a variance map catch defects that Otsu thresholding misses?
Otsu picks one global gray cutoff, so it only separates classes that differ in brightness. A missing-texture defect keeps the same gray values as its background; what changes is the local spread of those values. The variance map measures the spread inside a small window, so the patch stands out even though every one of its pixels sits at a normal gray level for that part.
Can I run variance filtering on a smart camera instead of a PC?
Only if the device's tool set includes a local variance or texture operator. Many vision sensors offer mean-based dynamic thresholding, which adapts a brightness cutoff and will not flag a uniform patch on a uniform background. If the tool list has no variance operator, pair an area scan camera with a PC library and build the map from two box filters.
How do I calibrate the rejection area for low-texture blobs?
Run at least 50 known-good parts, including the worst deformation cases, and record the largest low-variance blob on any of them. Set the reject limit at roughly twice that value and confirm it stays below the smallest real defect you must catch. In the worked example, good parts dipped to 0.8 mm², the target defect measured 2.16 mm², and the limit landed at 1.5 mm² - about 1.9 times the worst good part.
Does the method need mono or color imagery?
Compute the map on gray. A mono sensor avoids demosaicing, which averages color channels and smooths fine texture, lowering measured variance on small dots. On a color camera, convert to a single luma channel first; on repeating colored patterns, run the filter per channel and merge the masks so a dip in any one channel still flags.


