A part rarely arrives twice at the same angle and the same distance. When rotation and apparent size are both unknown, a pattern match has to search two extra pose dimensions. The classic way is an affine transform sweep: rotate the template, shrink it, warp it, score it, repeat. This page walks through that loop — 360° in 1° steps while a scale variable is divided by 1.005 on every pass — and the numbers that decide whether it works. Those numbers are the compounded scale landing at 0.166 after a full turn, how far a feature moves per pass, what nearest-neighbor interpolation costs and saves, and how to size the camera so the smallest scale still matches. Every figure is worked out, so you can retune the sweep to your own template and optics.
The affine chain: rotate, scale, warp
An affine transform maps every pixel through one matrix. That matrix can carry translation, rotation, scaling, shear and reflection together. A pose sweep needs two of those terms: a rotation about a center point and a uniform scale about the same center, composed into a single matrix per pass. Vision scripting environments expose the chain as four calls — build an identity matrix, append the rotation, append the scale, warp the image. The same matrix warps a binary mask through the region version of the warp call. Both matrices use the same pivot, so rotation and scale compose about one point. Set that pivot at the template's own center, or every rotated copy also swings around the image. If only the angle is unknown, the sweep reduces to a single rotation pass — the arbitrary-angle image rotation guide treats that case on its own.
The sweep usually runs on a preprocessed mask, not raw gray values. A threshold isolates the part, a morphological opening with an 11 px circular element removes speckle, and the image and the cleaned region go through the same matrix. If the matcher rejects the template itself as too small, fix the ROI before tuning the sweep — the template-image-too-small error has a direct fix.
// Phi: loop counter in degrees. Scale: real number, shrink factor per pass, starts at 1
Scale := 1
for Phi := 0 to 360 by 1
hom_mat2d_identity (HomMat2DIdentity)
hom_mat2d_rotate (HomMat2DIdentity, rad(Phi), 256, 256, HomMat2DRotate) // rotation about the image center (256, 256)
hom_mat2d_scale (HomMat2DRotate, Scale, Scale, 256, 256, HomMat2DScale) // uniform scale about the same center
affine_trans_image (Image, ImageAffinTrans, HomMat2DScale, 'nearest_neighbor', 'false')
// score ImageAffinTrans against the camera image and keep the best pose
Scale := Scale / 1.005
endfor
// check: after the loop, Scale = 1.005^-360 ≈ 0.166, because 1.005^360 ≈ 6.02
The scale line compounds: 1.0 to 0.166 in one turn
Dividing by 1.005 feels like a 0.5 % change per pass, but the factors multiply, so after n passes the scale is 1.005^-n:
One full turn therefore sweeps the template from full size down to 0.166 — a 6:1 linear range, 36:1 in area. That covers a part whose apparent size can fall to a sixth of the template. Set your own floor from the optics and the working-distance range rather than copying this one.
Two consequences follow. The sweep never magnifies: the scale only descends from 1.0, so a part that can also appear larger needs a second loop that multiplies — 1.005^n = 2 solves to n = ln 2 / ln 1.005 ≈ 139 passes per doubling. And the pass at 360° duplicates 0°, so the 361 passes deliver 360 distinct poses and one of them is free to cut.
One loop pairs each angle with exactly one scale
The diagonal is correct only when rotation and apparent size move together. It is harmless when the working distance is fixed by mechanics, so the scale loop only absorbs small drift. When angle and size vary independently, nest the sweeps. An inner scale loop under every angle multiplies the two pass counts: a 139-step scale range under a 361-step angle sweep is 361 × 139 = 50,179 transforms against 361. That multiplication is why commercial matchers run independent angle and scale ranges on an image pyramid internally, and why the coarse-to-fine scheme below matters.
Pass sizes: keep feature movement under one pixel
A scoring peak is about one pixel wide, so each pass must move the outermost template feature less than roughly one pixel. Rotation moves a point at radius r by r·Δθ; a scale factor k moves it by r·(k − 1):
At Δθ = 1° = 0.01745 rad, a feature 256 px from the rotation center jumps 256 × 0.01745 ≈ 4.5 px per pass — several pixels past its peak. A 512 px template can step over its best score. The scale step is finer by construction: k = 1.005 moves the same feature 256 × 0.005 = 1.28 px, close enough to the limit. Angle is the axis to tighten:
| Template radius r | Movement at 1° | Movement at 0.25° | Angle step to use |
|---|---|---|---|
| 57 px | 1.0 px | 0.25 px | 1° |
| 150 px | 2.6 px | 0.65 px | 0.5°–0.25° |
| 256 px | 4.5 px | 1.1 px | 0.25° |
The rule also bounds the scale factor: k = 1.005 respects the one-pixel limit up to r = 200 px (1 / 0.005). Beyond that, divide by 1.0025, which moves a 256 px feature 256 × 0.0025 = 0.64 px per pass. Going to 0.25° over a full turn costs 360 / 0.25 = 1,440 passes — four times the 1° loop. That is what the pyramid pass in the coarse-to-fine section is for.
Nearest neighbor or bilinear: what each buys
'nearest_neighbor' copies, into each output pixel, the single nearest source pixel: one read per pixel, no invented gray values, and edges snapped to the sample grid — the aliasing you see along rotated lines. Bilinear reads the four neighboring pixels and blends them, smoothing the warp and landing edges between samples. The price is about four times the memory traffic. The working split: run the whole sweep with nearest neighbor, where the score only has to point at the right neighborhood. Run one final warp bilinear at the winning pose, where sub-pixel edge position is what the score is made of.
Shrink adds a second effect. At scale 0.166 each output pixel covers 6.02 source pixels per axis — 36.3 source pixels per output pixel. Nearest neighbor keeps one and discards about 35, so a feature under roughly 6 template px can fall between two sampled rows and vanish. Keep every scored feature at least 1 / scale_min px across in the template.
Coarse-to-fine: the sweep fast and the pose sub-pixel
Run the search twice. The coarse pass runs on a pyramid level: reduce image and template by 4 per side, and a full 361-pass sweep costs 16 times less. The same 1° steps obey the one-pixel rule again — a 256 px radius becomes 64 px, and 64 × 0.01745 ≈ 1.1 px per pass. Take the best coarse hit, then re-warp its neighborhood — one coarse step either side in angle and scale — at full resolution with bilinear and steps of 0.05°–0.1°. A correct coarse peak sits within one coarse step of the true pose, so the fine pass is a handful of transforms, not another sweep. This is the scheme inside every commercial angle-and-scale matcher; the manual loop shows you where its run-time limits come from. When the frame rate still falls short, shrinking the search region usually beats shrinking the steps — shrinking the search area to lift the update rate has the numbers.
The cost of one sweep, worked
Take the loop exactly as written — a 512 × 512 template, 361 passes:
- pixels warped per pass: 512 × 512 = 262,144
- one full sweep: 262,144 × 361 = 94,633,984 ≈ 94.6 million output pixels
- nearest neighbor: one source read each, ≈ 94.6 million reads
- bilinear throughout: 4 reads and 3 blends each, ≈ 379 million reads — about 4 times the run time
- coarse pass at pyramid level 2, 128 × 128 per pass: 16,384 × 361 ≈ 5.9 million pixels — 16 times less
- nested grid instead of the diagonal: 361 × 139 = 50,179 transforms — 139 times the diagonal sweep
Two decisions fall straight out of the arithmetic. Bilinear everywhere quadruples the sweep to buy sub-pixel accuracy you need only once, at the final pose — spend it there, not everywhere. And warp the template rather than the whole frame whenever it is smaller: a 512 px template against a 4000 px frame warps 4000² / 512² ≈ 60 times less data per pass.
Size the camera for the smallest scale in the sweep
The smallest scale your search must reach sets a sensor requirement. The features you score on have to survive the worst case with at least 3 px across them — the same pixel rule that sizes camera resolution for defect detection:
Worked example: feature 0.5 mm, scale_min = 0.166, field of view 60 mm.
- pixel scale budget: 0.5 mm × 0.166 / 3 = 0.0277 mm/px
- minimum sensor width: 60 mm / 0.0277 mm/px ≈ 2,170 px
- a 4000 × 3000 USB3 sensor such as the LS-CMR-0166 clears that floor
- at 60 mm / 4000 px = 0.015 mm/px, the feature spans 0.5 mm / 0.015 mm/px = 33 px at full scale and 33 × 0.166 ≈ 5.5 px at the worst scale — clear of the 3 px floor with margin for a stable score
Enter your own field of view and smallest feature below, and the finder then lists area scan cameras with enough pixels.
Two USB3 sensors that carry this example with margin:
- Sensor format
- 1/1.7"
- Interface
- USB3
- Sensor format
- 1"
- Lens mount
- C-mount
The 12 MP camera meets the worked numbers directly. The 20 MP mono sensor is the pick when the feature is finer or the scale range deeper, because its extra pixels extend the reachable scale floor before the 3 px rule bites.
Where the loop runs, and the checks before you trust a hit
The sweep is processor work — warp, score, compare, hundreds of times per frame — so it belongs on a vision controller next to the cameras. A controller in this class pairs an Intel Core i3 with 8 GB of memory and 4 built-in light channels. One box grabs the frames, runs the sweep and strobes the illumination:
- LAN ports
- 5+ LAN ports
- USB 3 ports
- 3-4 USB 3 ports
- Light outputs
- 4 built-in channels
- Memory
- 8 GB
Checks before you trust the first hit:
- Template radius in pixels — set the angle step by the one-pixel rule (57 px → 1°; 256 px → 0.25°).
- Scale range from the optics and part presentation — shrink-only, or add the 139-pass magnify branch per doubling.
- Diagonal or grid — nest the loops when angle and apparent size vary independently.
- Smallest scored feature — keep it at 3 / scale_min px across in the template (3 / 0.166 ≈ 18 px at the worked scale floor).
- Interpolation — nearest neighbor for the sweep, bilinear for the winning pose.
- Symmetry — a rotationally symmetric part returns several equally good peaks; decide the pose policy first, as in symmetric part orientation matching in picking.
When the pose is found, the winning matrix is also the output: it carries the part's angle and scale straight to the robot or the reject station. To turn the sweep into hardware, start at the camera finder; heavier duty industrial mini PCs are an option when the controller line does not fit the cabinet. More processing-side reading sits on the systems topic page. Bring us a template size and a scale range and we will run the pass-count budget with you — contact the applications team.
Questions
Why does dividing the scale by 1.005 for 360 steps land at 0.166?
Each pass multiplies the previous scale by 1/1.005, so the factors compound instead of adding: after n passes the scale is 1.005^-n. One hundred passes give 1.005^-100 ≈ 0.607, and the full 360 give 1.005^-360 ≈ 0.166, because 1.005^360 ≈ 6.02. The sweep therefore covers a 6:1 linear size range — 36:1 in area — from full size down to one sixth of the template.
How do I choose the angle step for my template size?
Limit the movement of the outermost template feature to about one pixel per pass: Δθ ≤ 1/r radians, with r the template radius in pixels. That allows 0.95° for a 60 px radius, 0.38° for 150 px and 0.22° for 256 px; pick the next standard step below the limit — 1°, 0.5° or 0.25° — and note that 0.25° over a full turn is 1,440 passes, four times the 1° loop. A pyramid level makes the coarse steps legal again on the smaller image.
Does the sweep also find the part larger than the template?
No. Dividing by 1.005 only ever shrinks: the 360 passes cover scales 1.0 down to 0.166. To cover magnification, multiply instead — 1.005^n = 2 solves to n = ln 2 / ln 1.005 ≈ 139 passes per doubling. Run the shrink and magnify branches as separate loops, and if the part can do both, remember the grid multiplies too: 361 angles times 278 scale passes is over 100,000 transforms, which is what image pyramids are for.
What happens to small template features at the smallest scale?
At scale 0.166, one output pixel spans about 6.02 source pixels per axis — 36.3 source pixels per output pixel. Nearest-neighbor sampling keeps one of them and discards the rest, so a feature narrower than about 6 template pixels can fall between two sampled rows and vanish from the warped template. Keep every feature you score on at least 1/scale_min pixels across in the template — 6 px at scale_min 0.166, 20 px at 0.05.
Why does one loop with a running scale variable miss some poses?
Because it tests a diagonal, not a grid: pass 100 pairs the angle 100° with the single scale 1.005^-100 ≈ 0.607, and the pose (100°, 0.90) is never tried. If angle and apparent size vary independently, nest two loops — 361 angle steps times 139 scale steps, for example — or sweep scale separately inside each coarse angle bin, and budget for the multiplied run time with a pyramid.


