You have already thresholded the image, so the lighting work is done. What remains is geometry and one table lookup: find the black square borders, keep the near-square four-corner candidates, and read each candidate's bit grid against your dictionary. This guide gives the seven steps, the pixel budget that decides whether a marker can decode at all, and a worked sizing example with camera picks for moving markers, static scenes and PC-less stations.
What a detector actually matches
An ArUco marker is a square black border one cell thick around a bit grid: 4×4, 5×5 or 6×6 bits depending on the dictionary, so 6, 7 or 8 cells across including the border. Detection is three decisions per candidate. Is this blob a near-square quad? Do its interior cells read as bits? Does that pattern exist in the dictionary, in any of four rotations?
A wrong ID is rarely a matching failure. It is a candidate that was filtered out, or a cell that was sampled in the wrong place. OpenCV's aruco module runs exactly this pipeline; if your build lacks it, the CMake calibration build repair gets it back, and the same thresholds apply either way.
The pipeline, step by step
- Consolidate each marker into one blob. Label connected components (bwlabel) and fill each component's interior holes (imfill) so border plus bits become one solid region for area and solidity checks. Keep the unfilled copy too: the holes are the bits you will read later. This is the same labeling step behind finding circles in a binary image with contour analysis.
- Trace the outer contour of each blob's unfilled border ring. That closed curve is the candidate quad.
- Filter by shape. Keep a contour only when polygon approximation leaves exactly 4 corners, the filled area is at least 20×20 px (the smallest size field measurements confirm detectors catch), the minimum-area bounding rectangle has an aspect ratio between 0.8 and 1.25, and solidity is above 0.9. A border ring is solid black; anything ragged is noise.
- Order the corners identically every time. Sort the four corners by angle around the blob centroid and start from the corner nearest the top-left of the image. An inconsistent order scrambles the bit readout.
- Warp the candidate to a fixed grid. A perspective transform maps the quad to 8 px per cell, so a 6×6 marker becomes a 64×64 px patch wherever it sits in the frame.
- Sample the bits. Average a 3×3 px window at each interior cell centre and threshold at the midpoint of the two levels. If nudging the window half a cell flips a value, the marker is too small in the image — that is the pixel budget talking, not the code.
- Match against the dictionary. Pack the grid in the dictionary's bit order and compare with every entry in all four 90° rotations. Accept exact matches. Reject any candidate that matches in more than one rotation: ambiguous matches mean sampling is unreliable. Allow a one-bit error only where the dictionary documents that error distance.
Once an ID returns stable in four rotations, hand the pose to the ArUco pick-and-place cycle or publish it as a TF frame for hand-eye calibration.
The pixel budget: 10 px per bit cell
Detection floors are small. Field measurements on 3×3 and 4×4 markers put the smallest detected size at 20×20 px and a comfortable size at 30×30 px — roughly 3 to 6 px per cell with the border counted. The sampling literature explains the limit: as markers shrink, estimated bit centres grow sensitive to pixel quantization and land near cell boundaries. Design at double the comfortable figure:
A 6×6 marker therefore needs 60 px across its bit grid, 80 px with the border. This is the same pixels-per-target arithmetic as sizing a camera for crack detection, with the bit cell as the target.
Worked example: 20 mm 6×6 markers at a 400 mm field
Inputs: a 6×6 dictionary marker printed 20 mm wide including the border, so the bit grid is 15 mm; the 10 px per cell target; a camera resolving 4000 × 3000 px; and a station window 400 mm wide — a typical conveyor lane, and the one assumption in this example.
- Required marker span: 8 cells × 10 px = 80 px.
- Scale that requires: 80 px ÷ 20 mm = 4 px/mm.
- Scale delivered: 4000 px ÷ 400 mm = 10 px/mm, which is 2.5× the requirement.
- Marker as imaged: 20 mm × 10 px/mm = 200 px, so 200 ÷ 8 = 25 px per cell.
- Smallest 6×6 marker that still meets the budget here: 80 px × 0.1 mm/px = 8 mm.
- Widest field that keeps the 20 mm marker on target: 4000 px ÷ 4 px/mm = 1000 mm.
Decision: at a 400 mm field, print 6×6 markers 8 mm wide or larger, and a 20 mm print decodes at 25 px per cell with margin. Widen the field past 1 m and the same 20 mm marker falls to the floor; print bigger markers rather than moving the camera.
Which camera delivers the pixels
Three set-ups cover most marker stations.
- Markers moving on a belt or robot. You need a global shutter so the quad is not sheared during readout. The MV-CU020-90GC is a 2.5 MP color camera with a global shutter, C-mount and GigE interface.
- Small markers or a wide field. Resolution wins. The LS-CMR-0166 resolves 4000 × 3000 px at 30 fps over USB3 from a 12 MP 1/1.7" sensor; it is a rolling shutter model, so keep camera and scene still.
- No PC at the station. The MV-SC5060XM-08M-WBN is a 6 MP mono smart camera with an 8 mm M12-mount lens on GigE; component labeling and a dictionary lookup are light enough to run where the inspection runs.
- Sensor format
- 1/1.7"
- Lens mount
- C-mount
- Sensor format
- 1/1.7"
- Interface
- USB3
- Interface
- GigE
- Resolution
- 6-10 MP
Match lens to field in the camera configurator or filter the camera finder by shutter and resolution.
Failure modes, most common first
- The border vanished in the threshold. Glare on shiny or curved markers blanks the black ring, so there is no quad to find. Fix the illumination — diffuse or polarized light — not the code.
- The ID flips frame to frame. Bit centres land on cell boundaries; raise pixels per cell with bigger print, a narrower field or more pixels.
- The quad passes but matching fails. Check corner order first, then the bit packing order, then that all four rotations were tried.
- Motion shear. A moving marker under a rolling shutter skews; use a global shutter sensor or freeze motion with a short exposure and a strobed light.
When one marker fails mysteriously, dump its warped grid as a live binary image preview and read the bits by eye. The fault shows immediately.
Checks before you trust the detector
- Image one printed marker at the working distance; the ID must return in all four rotations.
- Measure marker width in pixels in a live grab and divide by cells across; the result must be 10 or more.
- Tilt the marker 20-30° and re-test. If the ID drops, raise the pixel budget before editing code.
- Test at the field corners, where perspective distortion is worst.
- Log pixels per cell with every read; a falling trend flags defocus, fouling or mounting drift.
Size the field and marker first, then pick the camera: the smart cameras category covers on-device inspection, the area scan cameras category covers PC-connected stations, and the systems topic page gathers the downstream guides from pose to grasp.
Questions
How small can an ArUco marker be in the image?
Documented field tests detect 3×3 and 4×4 markers down to 20×20 px, with 30×30 px comfortable — roughly 3 to 6 px per cell including the border. For a design that survives glare, tilt and print bleed, target 10 px per bit cell, and keep a white margin of at least one cell around the print so the border separates cleanly from the background.
Can a smart camera run this pipeline without a PC?
Yes. These steps — connected-component labeling, a four-corner filter, a warp to a small fixed grid and a dictionary lookup — are far lighter than AI defect models, and smart cameras are built to run this kind of rule-based inspection on the device itself. Pick a mono model with a global shutter when the markers move, and confirm the resolution supports your pixel budget at the working field width.
Why does the detector find the square but return no ID?
In order: the dictionary does not match the printed marker family; the corner order is inconsistent between candidates; the bit grid was sampled with too few pixels per cell, so cell centres drift; or matching tried a single orientation instead of all four rotations. Dump the warped grid as an image and read the bits by eye to separate the last two causes.
Do I need a global shutter camera for ArUco markers?
Only when the marker moves during exposure or readout. A static marker on a fixture reads like any still target, whatever the shutter type. Motion under a rolling shutter shears the quad enough to shift corners and break bit sampling; a global shutter sensor, or a short exposure paired with a strobed light, removes the shear at the source.


