A Hough transform counts binary pixels, so the threshold you feed it decides whether the scratch shows up as one clean line or the whole frame votes for phantom lines. When brightness moves between frames — different surface finish part to part, a drifting lamp, an auto mode quietly compensating — one hand-tuned gray level cannot serve every image, and a fixed percentage of full scale can drop a low-contrast scratch entirely. This how-to gives you the three answers that work on a real line: a global automatic threshold (Otsu or intermeans) computed per frame inside your own DLL, a moments-based or local threshold for thin scratches and uneven illumination, and the lighting hardware that stops brightness from moving in the first place. A worked example with full arithmetic shows why the automatic value survives a 40% brightness drop that breaks any fixed cut.
Why does one fixed threshold fail when image brightness changes?
One fixed threshold fails because brightness moves the whole histogram: raise the light by half and a scratch at gray 60 on a 150 background becomes gray 90 on a 225 background, so no single cut separates both pairs cleanly.
The 1.5× example stays inside the 8-bit ceiling of 255, which is exactly why doubling the light is rarely on the menu — headroom runs out and highlights clip. Below the threshold, the transform marks each pixel a line candidate and lets it vote into the accumulator.
With the threshold right, a scratch marks about one percent or less of the frame and the true line wins by a wide margin. With the threshold wrong, tens of percent of pixels vote, random alignments start to outvote the scratch, and the accumulator returns lines that exist nowhere on the part. The transform is not broken; its input is. When the marks are clean the transform itself still needs the right parameters — the Hough line detection for cracks guide covers when contour statistics beat the transform altogether.
How does Otsu pick a threshold for each image automatically?
Otsu picks the cut by testing every gray level from 0 to 255 and keeping the one that maximizes the between-class variance of the two classes it creates, so the threshold moves with the histogram automatically.
Both Otsu and intermeans need nothing but the 256-bin histogram, so they cost one pass over the image plus a scan of at most 256 candidate cuts. The 256-cut scan is microseconds; the histogram pass dominates, and folding it into the pass that already walks the pixels makes it nearly free.
Intermeans is the lighter cousin: start from the overall mean, compute the two class means, set the threshold to their midpoint, repeat until the value stops moving — on a two-peak histogram that takes a handful of iterations. Either method runs per frame inside the same DLL that calls the transform, so the threshold becomes a computed property of each image rather than a constant you maintain. The criterion and its derivation are summarised on Wikipedia's Otsu's method page.
Why does Otsu miss a scratch that covers less than one percent of the pixels?
Otsu assumes two sizeable classes; a scratch covering well under one percent of the pixels barely registers in the histogram, so the maximum between-class variance can land on shading edges instead of the scratch.
Do the arithmetic for a typical frame: a scratch 400 pixels long and 3 pixels wide marks about 1200 pixels of a 12 megapixel image — 1200 / 12,000,000 = 0.01% of the frame. A class that small cannot shape the criterion, and the documented weak spots of Otsu are exactly this: heavy noise, small objects and inhomogeneous lighting.
Three fixes, in the order to try them. First, restrict the histogram to the ROI where scratches are possible; the scratch share rises and Otsu behaves. Second, switch the criterion: a moment-preserving threshold keeps the gray image's intensity moments in the binary result and tolerates extreme class-size imbalance better. Third, accept the automatic value as a starting point and shift it 4-5 gray levels toward the background until the binary line is continuous and background speckle stays out — tune that shift once per product family on 20-30 known parts, then lock it. The scratch defect detection with lighting and dynamic thresholding guide pairs this decision with the lighting that creates the contrast in the first place.
Can a local threshold handle a part lit unevenly across its width?
A local threshold computes the cut inside a small window, typically 31 × 31 to 61 × 61 pixels for line work, so a brightness gradient of 50 gray levels across the part no longer hides a scratch with 30 levels of local contrast.
The cut per pixel is the local mean minus an offset, so it follows the shading instead of fighting it. Two starting values matter. The window must be several times the line width — 3-5× as the floor, which for a 3-5 pixel scratch puts you in the 31 × 31 to 61 × 61 range. The offset subtracts 5-10 gray levels so background texture does not flip to line. A summed-area table makes each windowed mean a constant-time lookup, so the pass stays fast even in hand-written C.
Watch the two failure modes: a window so small that the scratch becomes its own background and erases itself, and a window so large that the method quietly becomes the global threshold it was meant to replace. The dark region extraction guide shows the windowed route end to end on unevenly lit parts.
What does the threshold become on two images of the same part?
On a reduced 1000-pixel histogram the arithmetic commits: Otsu returns 120 for the bright frame and 72 for the same part at 60% brightness, while either value used as a fixed cut destroys one frame or the other.
Frame A is the bright one: background peak at gray 160-180, scratch at gray 60-120. Frame B is the same part with the light current or exposure down 40%, so every level scales by 0.6. Seven populated bins carry the story:
| Gray level, frame A | Pixels | Gray level, frame B | Pixels |
|---|---|---|---|
| 60 | 30 | 36 | 30 |
| 80 | 15 | 48 | 15 |
| 100 | 10 | 60 | 10 |
| 120 | 20 | 72 | 20 |
| 140 | 40 | 84 | 40 |
| 160 | 300 | 96 | 300 |
| 180 | 585 | 108 | 585 |
Totals: 1000 pixels per frame; Σ(level × count) = 165,300 for A and 99,180 for B, grand means 165.3 and 99.2. One full row of the criterion, frame A at T = 120: ω0 = 75/1000 = 0.075; μ0 = (1800 + 1200 + 1000 + 2400)/75 = 6400/75 = 85.3; μ1 = (165,300 − 6400)/925 = 171.7; σ²B = 0.075 × 0.925 × (171.7 − 85.3)² ≈ 517. Scanning every candidate bin gives:
| T, frame A | σ²B | T, frame B | σ²B |
|---|---|---|---|
| 80 | 458 | 48 | 165 |
| 100 | 499 | 60 | 180 |
| 120 | 517 | 72 | 187 |
| 140 | 483 | 84 | 174 |
| 160 | 305 | 96 | 110 |
The maxima sit at T = 120 for frame A and T = 72 for frame B — the automatic cut follows the 40% brightness drop exactly. Now watch a fixed cut die both ways. Apply 120, tuned on the bright frame, to frame B: the background bins at 96-108 are also below 120, so all 1000 pixels become line candidates and the accumulator fills with phantom votes. Apply 72 to frame A: only the 30 pixels at gray 60 survive, most of the scratch is gone, and no line collects enough votes to pass a minimum. Compute the cut per frame and both scratches come through whole. Real frames add texture between the peaks, but the criterion behaves the same — the gap in this reduced histogram just makes every row checkable by hand.
How do you stop noise pixels from voting for phantom lines?
Only pixels that survive the threshold should reach the transform: clean the binary image, narrow the accumulator's angle range to directions the part can actually produce, and demand a minimum vote count before a peak is called a line.
Work the chain as numbered steps:
- Build the 256-bin histogram of the inspection ROI and run Otsu or intermeans to get T for that frame.
- If the binary scratch breaks into dashes, shift T by 4-5 gray levels toward the background; if background texture appears, shift back until it disappears. Lock the shift after tuning on 20-30 known parts.
- Despeckle: delete dark blobs under about 20 pixels and keep only elongated marks, because a real scratch is long relative to its width.
- Restrict the Hough angle search: all 180° when scratches can run any way, or a ±15° window around the extrusion or grinding direction when process knowledge allows — fewer bins, fewer phantom peaks, and a faster DLL. Narrow ROI and angle range also fix the other common complaint, slow accumulation; the slow Hough line detection troubleshooting guide walks the speed side.
- Set minimum votes from geometry: a scratch 200 pixels long and 3 pixels wide marks about 600 pixels (200 × 3), so require at least 300 votes per peak and tune from there.
- Validate on 50 known-good and 50 known-scratched parts: good parts must return zero lines, scratched parts must return the scratch every time.
When is fixing the lighting better than a smarter threshold?
Fix the light first when brightness varies frame to frame on the same part under the same program: a 100 Hz ripple from 50 Hz mains lamps shifts the histogram between frames, and no threshold choice repairs an exposure that sampled different light.
Run the separating test before writing any code. Capture 20 frames of one part with fixed settings and watch the background peak: if it wanders more than 2-3 gray levels, the illumination or exposure is the variable and software will chase it forever. If the peak sits still but different parts sit at different levels — finish, reflectivity, alloy — that is genuine part-to-part variation, and an automatic threshold is the right tool.
The usual hardware culprits, in order of how often they bite: an unfiltered mains-driven light, whose output ripples at twice the mains frequency — 100 Hz on 50 Hz mains, a 10 ms cycle; an exposure shorter than one ripple cycle, which samples a different phase each frame, so set exposure to a multiple of 10 ms at 50 Hz mains or 8.3 ms at 60 Hz; and any automatic exposure or gain, which re-brightens the image whenever a part edge enters the frame. The frame-to-frame brightness flicker guide and the auto-mode drift guide cover both failure modes in detail.
Geometry comes before all of it. A dark scratch on a bright, even background is bright-field geometry: diffuse light along and around the viewing direction, so the flat surface returns light to the camera and the scratch scatters it away. The bright field vs dark field guide works through the reflection law that decides which side of that line your part sits on, and on shiny metal a cross polarization filter pair removes the specular glare that would otherwise dominate the histogram.
What hardware keeps brightness repeatable frame to frame?
The repeatable-brightness chain is a strobed light, a controller that fixes the pulse energy, and a camera on manual exposure and gain, so the background peak lands in the same gray-level bin on every frame and one threshold serves all of them.
Start with the light geometry. A diffused white bar mounted above and across the part gives the bright-field background that keeps the histogram peak tight, and a bar wider than the field of view holds the background even to the frame edges. The LS-BAR-1276 is a 372 × 28 mm diffused white unit that runs on 24 V. Browse the bar lights category or the light finder for other lengths.
- Size
- 200-400 mm
- Type
- Diffused
- Voltage
- 24 V
The controller is the half that makes brightness a constant rather than a hope. In strobe mode the pulse energy is set once, the mains phase stops mattering, and every frame receives the same photons. The LS-CTL-0070 is a 4-channel, 200 W digital box with 24 V constant-voltage outputs and RS-232 control, and strobe is among its supported features — enough channels to run main light, backlight and a second station from one box. Compare the rest of the light controllers range if you need more channels or constant-current outputs.
- Output
- Constant voltage 24 V
- Channels
- 4 channels
- Control
- Digital
- Feature
- Strobe
Close the chain on the camera side: exposure and gain on manual, auto modes off, and a mono sensor so the histogram counts true intensity rather than interpolated color. The MV-CS200-10UM is a 20 MP mono USB3.0 camera with a 1-inch IMX183 sensor and a C-mount. The area scan cameras finder filters by sensor format and interface when your resolution budget differs.
- Sensor format
- 1"
- Lens mount
- C-mount
- Interface
- USB3
- Resolution
- 16-25 MP
Which fix do you choose for your inspection?
Choose by the variation you actually measure: Otsu per frame for part-to-part brightness differences, moments or local thresholds for thin scratches and uneven illumination, and strobed hardware for frame-to-frame drift, then hold one threshold once the histogram stops moving.
- Background peak wanders more than 2-3 gray levels across frames of the same part: fix the chain — strobe, manual exposure, no auto modes — and re-test. A fixed threshold becomes safe; keep the automatic value running as a logged drift guard.
- Background stable, brightness differs part to part: compute the cut per frame (Otsu or intermeans) inside the DLL, with the 4-5 level shift tuned per product family.
- Otsu lands on shading instead of the scratch: restrict the histogram to the ROI or switch to a moment-preserving threshold; go local (windowed) when illumination is uneven across the part.
- Scratch thinner than about 2 pixels or peak separation under ~15 gray levels: no threshold rescues that — change the lighting geometry or add cross polarization, then re-run the histogram.
That last case is the honest limit: a threshold can only separate what the optics delivered. When the chain is right, the whole problem reduces to one per-frame computation your DLL already has the data for. Start at the lighting step of the configurator to match light, controller and camera to the part, or send us the two extreme frames through the contact page and we will help you pick the chain.
Questions
How long does the automatic threshold add to each frame in a C DLL?
One histogram pass over the ROI plus a scan of 256 candidate cuts. At 4000 × 3000 the pass is 12,000,000 bin increments — single-digit milliseconds in C — while the candidate scan is microseconds. Fold the histogram into the pixel pass you already run before the transform and the extra cost approaches zero.
Can moments-based thresholding and Otsu disagree on the same image?
Yes, and by several gray levels: Otsu maximizes the separation between two classes while a moment-preserving threshold keeps the gray image's intensity moments in the binary result. Trust neither by default — keep the one whose binary image shows the scratch continuous with no background speckle, tune the shift once per product family, then lock the choice in the DLL.
What strobe pulse width suits scratch inspection?
On a stationary part, any pulse the controller regulates cleanly works — a few hundred microseconds to a few milliseconds is typical — because brightness then follows the pulse energy, not the mains phase. On a moving part, keep blur under one pixel: blur in pixels equals part speed in pixels per millisecond times pulse width in milliseconds, so a part moving 2 px/ms needs a 0.5 ms pulse or shorter.
Do I still need an automatic threshold once the lighting is strobed and stable?
Keep it as a guard. Stable hardware narrows the frame-to-frame spread to a gray level or two, so a fixed threshold becomes safe, but the automatic value costs almost nothing and doubles as a drift alarm: log it per frame and raise a warning when it moves more than a few levels from the commissioning value.
Does a color camera change how you threshold for Hough line detection?
Convert to one gray channel before any histogram work; a demosaiced color image gives each pixel an interpolated value that smears the histogram. A mono sensor measures true intensity per pixel, so the scratch-to-background separation in the histogram is what the optics actually deliver. Color earns its cost only when the scratch itself has a color signature the background lacks.


