Code readingHow-to

OCR ROI follows a moving part: anchor it to a pattern match

Anchor your OCR ROI to the pattern match so it follows the moving part: the rigid transform, a worked pixel example, and the bench checks that make it hold.

LumaSpec Vision12 min read Share on LinkedIn

On this page 10 sections
  1. 01Why an offset is not enough
  2. 02What you need
  3. 03Teach the anchor once
  4. 04Re-anchor every image
  5. 05Worked example: a 200 × 40 px serial ROI re-anchored to a match at (1680, 960) tilted 7°
  6. 06Prove it before the line sees it
  7. 07Common mistakes that break the anchor
  8. 08When a fixed ROI is still the right answer
  9. 09Parts that do this on the line
  10. 10Questions

Sticker text moves. The label lands a few millimetres off, an operator re-places a part by hand, a fixture loosens — and a fixed OCR window starts clipping the serial number. You already have the pattern match: it finds the sticker and reports its position and angle. What is missing is the step that drags the OCR ROI along with it, rotation included. This guide shows how to anchor the read window to the match, through the coordinate-system feature your vision software probably has and through the corner math for when it does not, and how to prove the anchor holds before the line runs.

Why an offset is not enough

The tempting method, and the one most people try first, is to record the OCR ROI's offset from the match center in the golden image, then add the runtime center to those offsets on every cycle. It follows translation perfectly. It follows rotation not at all: the offsets live in the part's frame, so once the sticker tilts, a window shifted in image coordinates still points in the old direction. The error grows with window width and tilt together. At 7° of tilt, a 200 px wide window swings 200 × sin 7° ≈ 24 px from one end to the other — enough to clip the first and last characters, which on a serial number are usually the ones that matter.

Vision software has a name for the correct answer: a fixture, or reference coordinate system. You define the read window once, relative to the reference feature, and the library re-projects it through every match result, translation and rotation together. The major vision platforms all document this same pattern: link the ROI to a coordinate system whenever the part moves. If your tool exposes it, use it; the sections below show what it does internally, which is also your path when it does not. Getting the reference origin itself right is a separate skill — see pattern matching reference coordinate system: fix your origin.

What you need

  • A golden image: part at the nominal pose, in focus, lit exactly as it will run.
  • A pattern match trained on a feature that belongs to the sticker or part — a logo, a notch, a printed corner — and asymmetric enough that it cannot flip 180°.
  • The match result for every image: center (x, y) in pixels and angle θ in degrees or radians.
  • The OCR ROI from the golden image, as left, top, right, bottom in pixels.
  • An OCR step that accepts either a rotated rectangle or an ROI tied to a coordinate system. Code reader jobs usually offer a relative-to-reference mode; PC-side libraries take corner points and expect you to compute them.
  • Parts set to the extremes of the position and tilt envelope, for bench tests.

Teach the anchor once

  1. Acquire the golden image and train the match on it. Record the reference center c0 = (c0x, c0y) px. The template angle here is 0 by definition; only offsets from this pose get stored.
  2. Draw the OCR ROI around the text in that same image and record L, T, R, B in pixels.
  3. Convert the corners to offsets from c0: (L−c0x, T−c0y), (R−c0x, T−c0y), (R−c0x, B−c0y), (L−c0x, B−c0y). Store these four offset pairs with the job; they never change.
  4. Set the match's expected angle range to cover the real envelope, and put a floor under the accepted score so a weak match cannot steer the window — the coarse search gates everything downstream (pattern match score threshold: the coarse search gates the result).
  5. If your OCR step supports ROI-relative-to-coordinate-system, define the window in that mode and stop here: the software performs exactly the transform in the next section.

Re-anchor every image

  1. Acquire and run the match. Read the runtime center c1 = (x, y) px and angle θ. If your trig functions expect radians, convert with θrad = θdeg × π / 180.
  2. Transform each stored corner offset (dx, dy) into the rotated offset:
x' = dx·cosθ − dy·sinθ y' = dx·sinθ + dy·cosθ
  1. Add c1 to each rotated offset and round to whole pixels — a pixel of rounding sits well inside any sane margin. Feed the four corners as the OCR ROI. If your OCR step only takes axis-aligned rectangles, feed the bounding box of the four corners; the worked example below shows the size penalty.
  2. If the match fails or scores under the floor, output a no-read. Do not read with the previous cycle's window.

Image rows grow downward, so with this formula a positive θ rotates the window clockwise on screen. Some tools report the opposite sign; check once with a known tilt and confirm the window leans with the text before trusting the signs.

Worked example: a 200 × 40 px serial ROI re-anchored to a match at (1680, 960) tilted 7°

Inputs, and where each comes from:

  • Teach run, golden image: match center c0 = (500, 400) px; OCR ROI left 560, top 385, right 760, bottom 425 px — a 200 × 40 px window around the serial line. Stored corner offsets: top-left (60, −15), top-right (260, −15), bottom-right (260, 25), bottom-left (60, 25) px.
  • Runtime match output for the image being read: c1 = (1680, 960) px, θ = −7° (the sticker tilted clockwise on screen).
  • Trig values, from any calculator: cos 7° = 0.9925, sin 7° = 0.1219, so for θ = −7°: cos θ = 0.9925, sin θ = −0.1219.

The rule in words: move the window's corners from the teach center to the runtime center, and rotate them about that center by the match angle — a rigid transform, nothing else.

Top-left corner, offset (60, −15) px:

x' = 60 × 0.9925 − (−15) × (−0.1219) = 59.55 − 1.83 = 57.72 px y' = 60 × (−0.1219) + (−15) × 0.9925 = −7.31 − 14.89 = −22.20 px Runtime corner: (1680 + 57.7, 960 − 22.2) = (1737.7, 937.8) → (1738, 938) px

Top-right corner, offset (260, −15) px:

x' = 260 × 0.9925 − (−15) × (−0.1219) = 258.05 − 1.83 = 256.22 px y' = 260 × (−0.1219) + (−15) × 0.9925 = −31.69 − 14.89 = −46.58 px Runtime corner: (1936.2, 913.4) → (1936, 913) px

Corner Offset at teach (px) Rotated offset (px) Runtime corner (px)
Top left (60, −15) (57.7, −22.2) (1738, 938)
Top right (260, −15) (256.2, −46.6) (1936, 913)
Bottom right (260, 25) (261.1, −6.9) (1941, 953)
Bottom left (60, 25) (62.6, 17.5) (1743, 978)

Offset-only anchoring would have parked the top-left corner at (1680 + 60, 960 − 15) = (1740, 945) px — 2 px right and 7 px below the anchored corner, a 7 px clip in a 40 px band. And the tilted text no longer fits the original box at all: rotated 7°, the 200 × 40 px window spans 200 × sin 7° + 40 × cos 7° ≈ 24.4 + 39.7 = 64 px of height. An axis-aligned reader needs the bounding box, 200 × cos 7° + 40 × sin 7° ≈ 203 px wide by 65 px tall — and that extra margin can reach neighboring print, so the text-line constraints then have to do the policing.

Decision. This sticker is hand-placed, so tilt is part of the envelope: transform the corners, every image. If instead your fixture held the part within ±1°, a 200 px window swings only 200 × sin 1° ≈ 3.5 px end to end, and an offset-only window with a 5 px pad reads fine. The commit point for a window this wide is about 2°: 200 × sin 2° ≈ 7 px, which eats most of a 5 px pad. Below that, offset-only; above it, the rigid transform.

Prove it before the line sees it

  1. Put a part at each extreme: the four envelope corners plus the center, at 0° and at maximum tilt in both directions. Nine poses minimum for a rectangular envelope.
  2. Read 50 times per pose. Sign off only on runs with zero substitutions — a wrong-but-confident string is worse than a no-read — and no more than the occasional no-read at the worst pose.
  3. Test the fault path on purpose: cover the sticker or remove the part, and confirm the PLC receives a no-read, never the previous result.
  4. Repeat the run after any change to lens, working distance or part height, because pixel offsets are only valid at the taught height.

Common mistakes that break the anchor

  • A symmetric match feature — a circular logo, a square cut-out — flips 180° and the window lands on the wrong side of the sticker. Pick an asymmetric feature or add a second, offset check (pattern match flips 180 degrees: fix false hits and tolerance settings).
  • Degrees fed to a radians function: 7 handled as radians is 401° ≈ 41° of rotation, and the window leaves the sticker entirely.
  • Offsets measured on a zoomed preview. Teach and run in full-resolution pixels, or the scale error lands in every window.
  • Part height drift: moving the part away from the lens changes magnification, so pixel offsets stop matching millimetres of label. Fixture the height, or calibrate and work in millimetres (rotation and scale affine transform search in machine vision).
  • A window drawn tight to the glyph edges leaves no room for the residual pixel of anchor error, and small text has a floor anyway (minimum font size for OCR: why small text fails and how to fix it).
  • Reading with the match score under the floor because it usually still reads. That is the stale-anchor failure with extra steps; when a trained job misreads at runtime, the cause is usually a parameter, not the anchor (OCR trains but fails at runtime: set every parameter).

When a fixed ROI is still the right answer

If a hard stop pins the part and the label is applied by machine, measure before you build the anchor: log the match center and angle over 20 or so production parts. If the worst tilt keeps the end-to-end swing under your pad — under about 2° for the 200 px window and 5 px pad above — a fixed ROI with that pad is fewer moving parts and one less thing to misconfigure. The anchor earns its keep the day placement becomes manual or the fixture wears, so store the offsets in the job even while the fixed window runs.

Parts that do this on the line

If the sticker sits at one distance and the whole job — match, anchor, read — belongs on the sensor itself, fixed code readers and smart cameras run it without a PC. Reading several strings off one sticker is its own set-up; OCR on a fixed code reader: multiple text strings in one image covers it. The MV-ID3013PM-12M-WBN carries a 12 mm focal length lens and 1.3 MP resolution, a close-range pairing for label text.

Fixed code readersHikrobot MV-ID3013PM-12M-WBN 12mm focal length 1.3 MP Compact Industrial Code ReaderMV-ID3013PM-12M-WBN
Interface
Fast Ethernet
Resolution
Up to 3 MP
Shutter
Global
Lens mount
M12
$1,979.00View part

Small characters or a wide envelope shift the choice toward resolution, because more pixels make any rotation error a smaller fraction of the character height. The MV-ID5120RM-08M-RBN resolves 12 MP over GigE.

Fixed code readersHikrobot MV-ID5120RM-08M-RBN Industrial Code ReaderMV-ID5120RM-08M-RBN
Interface
GigE
Resolution
12 MP
Shutter
Rolling
Lens mount
M12
$1,969.00View part

When the corner math runs in your own code, a vision controller takes the camera and the PLC link in one box. The MV-VC3301H-128G66 runs the loop on an Intel Core i3 with 8 GB of memory and has 4 built-in light channels for the label light.

Vision controllersHikrobot MV-VC3301H-128G66 I3-8100, 8GB+128G SSD, No PoE, PCIe expansion supported, Incl. VM6200Pro licenseMV-VC3301H-128G66
LAN ports
5+ LAN ports
USB 3 ports
3-4 USB 3 ports
Light outputs
4 built-in channels
Memory
8 GB
$2,419.00View partBuild a system with it

To size the whole chain, start at the camera and code reader finder or go straight to the system configurator. The other text-reading setups live on the code-reading topic page, and the contact page is the direct line if you want a second pair of eyes on your offsets before ordering.

Questions

My match tool returns position but no angle — can I still anchor the ROI?

Train a second match feature next to the first. The line between the two centers carries the tilt: θ = atan2(y2 − y1, x2 − x1) in pixels, in the same sign convention as your software's angles. Anchor the corners to feature one's center exactly as in the transform, then rotate by that two-point angle. Many code reader jobs accept a two-point reference natively — look for it before writing the math yourself.

How much margin should the OCR window leave around the characters?

Pad each side by a quarter to half the character height as a starting point. Segmentation needs quiet space around the glyphs to split them cleanly, and the pad also absorbs the residual pixel or two of anchor error. Too much margin is its own failure: a window twice the text height can catch the neighboring line of print and turn it into substitutions. Start around 25-50 % and widen only if bench poses clip characters.

Does ROI anchoring work on a smart camera or only on a PC system?

Both, provided two things exist: the match result exposes position and angle, and the OCR step accepts its window relative to a reference. Smart cameras and code readers usually give this as a relative-to-reference option inside the job; PC libraries hand you the raw match result and expect you to compute corners yourself. When neither path exists, the fallback is the same four-line transform in your own code, feeding a rotated rectangle if the tool takes one.

What should the system do when the match fails on a given image?

Fail the read: skip OCR, send a no-read to the PLC, and log the image with its score. Never fall back to the previous cycle's window — a stale anchor points at whatever now occupies the old spot, and its text can enter your records as a good read. A no-read costs a retrigger or a quick glance; a substitution ships wrong data downstream, which is the failure that costs real money.

Why does the anchored window still miss the text when the part sits farther away?

Pixel offsets are only valid at the height you taught them. Distance changes magnification, so a stored 60 px offset no longer spans the same millimetres of label and the window lands short of the text. Fixture the part height so it cannot drift, or calibrate the camera and carry the match result and offsets in millimetres instead of pixels — then the transform holds across distance as well as rotation.