A defect call that keeps landing on the background, a print band that runs past the feature, a busy surface that feeds the algorithm pixels it should never see — most of these are ROI problems, and the cure is a mask image your code obeys. Here you will build the simplest useful mask: a black image the same size as your camera image, with one filled circle of value 255 where the part sits. The recipe works in any vision library, because every one exposes the same three primitives: create and fill an image, draw a filled circle, count pixels. It runs the same on a smart camera and on a vision controller, and you will verify it with arithmetic before it touches your inspection code.
What you need
Three inputs and one output:
- The working image's width and height in pixels. The mask copies both, so mask pixel (x, y) is image pixel (x, y).
- The circle's center and radius, in pixels. If the drawing gives you millimetres, convert first — the formula sits under Common mistakes.
- One 8-bit single-plane image for the mask itself: 0 means ignore, 255 means process, and nothing in between.
A mask is data for the algorithm; an overlay is a drawing for the operator. Keep the two apart from the first line of code. If the ring must be visible on screen or in the saved image, that is an overlay — the circular ROI overlay article covers that side.
Build the mask in four steps
- Create the mask image at the working image's width and height, one 8-bit plane. On a 640 × 480 image the mask is 307,200 bytes — a third of the 921,600-byte color frame beside it.
- Set every pixel to 0. If the create call does not guarantee zero-fill, run an explicit initialize or a one-line fill loop. A mask that starts with garbage quietly processes pixels you never chose.
- Draw a filled circle of value 255 at the center and radius, with anti-aliasing off on this plane. A rim value of 128 is neither inside nor outside, and threshold logic downstream reads it differently on every pass.
- Leave the mask on its own plane. If you need a permanent record of where you inspected, burn the overlay into a copy afterwards — the overlay-to-pixels technique shows how — and keep the mask clean.
If the library's fill-circle call misbehaves, or you want zero dependencies, the entire mask is one distance test over a raw pixel buffer:
// library-free: works on any 8-bit pixel buffer
int width = 640; // mask width in pixels
int height = 480; // mask height in pixels
byte[] mask = new byte[width * height];
for (int i = 0; i < mask.Length; i++) { mask[i] = 0; } // the black image
int centerX = 320; // circle center, x in pixels
int centerY = 240; // circle center, y in pixels
int radius = 100; // circle radius in pixels
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int dx = x - centerX;
int dy = y - centerY;
if (dx * dx + dy * dy <= radius * radius) {
mask[y * width + x] = 255; // the filled circle
}
}
}
Verify the mask with a pixel count
The count closes the loop before the mask reaches a production recipe. The expected area is a circle's, and the only real uncertainty is the one-pixel boundary ring:
Worked example: 640 × 480 mask, center (320, 240), radius 100.
- Image area: 640 × 480 = 307,200 pixels.
- Expected mask area: π × 100² = 31,416 pixels, which is 31,416 / 307,200 = 10.2% of the image.
- Accept 30,788 to 32,044 pixels: ± 2π × 100 = ± 628 pixels, or ± 2.0%, the width of the boundary ring where fill conventions differ.
- Probe pixels: (320, 240) reads 255; (419, 240) reads 255 at distance 99; (421, 240) reads 0 at distance 101. The pixel at exactly 100 — (420, 240) — belongs to the circle under the ≤ convention, and either convention stays inside the ± 628 band.
- The corner (0, 0) sits at √(320² + 240²) = √160,000 = 400 pixels from the center, so it must read 0.
The test itself is three lines:
int inside = 0;
for (int i = 0; i < mask.Length; i++) { if (mask[i] == 255) inside++; }
// this example: accept 30788 to 32044
The relative tolerance is 2/r, so it tightens as the circle grows: about ± 20% at r = 10 and ± 2% at r = 100. On a small circle, trust the probe pixels and treat the count as loose. A count near 125,664 — exactly four times 31,416 — means the diameter went into the radius argument, because area scales with r².
Use the mask
Three jobs, in the order most inspections need them:
- Restrict a search. Point the defect scan, print check or matcher at the 31,416 masked pixels instead of all 307,200. Where the work is per-pixel, that is a 10× shorter loop over the 10.2% of the image that can actually pass or fail.
- Hard-zero the outside. Multiply or logical-AND the image by the mask, pixel by pixel: everything outside goes to 0 and a downstream threshold can only fire inside the circle. This pairs naturally with the thresholded-binary workflow and, while you tune it, the live binary preview.
- Hand over coordinates. Enumerate the masked pixels into a point list for a centroid or measurement step — the ROI pixel coordinate extraction article walks exactly that loop.
When the part moves, measure the center and radius every cycle instead of hard-coding them: threshold a coarse image, fit a circle to the part contour (the contour circle detection article shows the fit), then draw the mask from the measured numbers plus a margin of about 2-5% of the radius.
Common mistakes
- Swapped coordinates. Draw once with the center at (100, 200). If the dot lands at (200, 100), you and the library disagree on argument order; feed the pair the way the function expects and re-run the probe pixels.
- Diameter in the radius slot. The count catches it: four times the expected area is a twofold radius error, because area scales with r².
- A gray rim. Anti-aliasing, or a mask stored as a JPEG, leaves values like 41 and 214 on the boundary. Read the plane's minimum and maximum: a valid mask holds exactly 0 and 255.
- A fixed mask on a moving part. It passes the empty fixture and fails the loaded one; measure per cycle as the previous section shows.
Radius given in millimetres is the final classic — convert before drawing:
A 64 mm field across 2592 pixels gives 2592 / 64 = 40.5 px/mm, so a Ø 20 mm part — a 10 mm radius — draws at 10 × 40.5 = 405 pixels. Still choosing the camera for that part? The camera-resolution calculator runs the same conversion from the other side:
When a filled circle is the wrong shape
Same recipe, different fill call:
- Rectangular parts take a filled rectangle or polygon: initialize to 0, fill with 255, and the count check becomes width × height of the region.
- Ring-shaped features — a thread crest, a gasket face, a washer — take an annulus: fill the outer circle with 255, then fill the inner circle with 0 on top. With an outer radius of 100 and an inner radius of 40, the area is π × (100² − 40²) = π × 8,400 = 26,389 pixels.
- Several regions can share one mask: each filled circle ORs into the union and overlaps stay 255. If the regions must be reported separately, either keep the centers at least the two radii apart or build one mask per region — the multiple ROI display article handles showing them.
Where the mask runs
The mask logic is identical wherever it lives; only the address space changes.
Threshold-mask-count inspection fits entirely on a smart camera, which is what the smart cameras category is organized around; the camera finder filters by resolution, shutter, color and mount. The MV-SC5060XM-08M-WBN carries a 6 MP mono sensor with an 8 mm M12-mount lens over GigE — the classic shape for a mask-and-count check inside the camera.
- Interface
- GigE
- Resolution
- 6-10 MP
- Sensor format
- 1/2.4"
- Shutter
- Rolling
When color carries the feature — a missing print band, a dye lot — the MV-SC3050XC-16M-ATN-H brings a 5 MP color sensor in a 1/1.45" format with global shutter, an M12 mount and Fast Ethernet.
- Interface
- Fast Ethernet
- Resolution
- 5 MP
- Sensor format
- 1/1.45"
- Shutter
- Global
Library code you write and keep wants a controller beside the line. The vision controllers category is that path; the MV-VC2140-128G40-1T runs on an Intel N97 with 8 GB of memory, 128 GB SSD plus 1 TB HDD storage and four built-in light channels — and the mask, the circle fit and the count check are ordinary code on it.
- LAN ports
- 3-4 LAN ports
- USB 3 ports
- 2 USB 3 ports
- Light outputs
- 4 built-in channels
- Memory
- 8 GB
This recipe is rule-based on purpose; where rules end and a trained model earns its keep, the AI versus rule-based comparison sets out the split. To size the camera side from scratch, start here:
The configurator matches a camera, lens, light, light controller and cables to each other, step by step, and lists only parts that fit what you already picked.
Checklist before you ship
- Mask width and height equal the image's, every cycle — a resolution change moves every coordinate.
- Exactly two values on the plane: minimum 0, maximum 255.
- Pixel count inside expected ± 2πr.
- Probe pixels: center reads 255; center + radius + 1 reads 0.
- Stored, if at all, as BMP or PNG — never JPEG.
- Center and radius measured per part whenever the part can move.
- Overlay drawings live on a copy; the mask plane stays untouched.
When the recipe changes camera, lens or resolution, re-run the count check before the first production shift — center, radius and tolerance all move with the pixels. The neighbouring image-preparation recipes sit on the systems topic page, and for help matching the mask approach to your part, the contact page reaches the applications team.
Questions
Should mask pixels be 0 and 1, or 0 and 255?
For an 8-bit mask use 0 and 255. Full-scale white previews correctly, survives arithmetic, and every drawing function treats 255 as on. Use 0 and 65535 on a 16-bit plane for the same reasons. A 0/1 mask previews as an all-black image, and code that multiplies by the mask crushes the inside toward zero instead of passing it through.
How do I size the circle around a feature that is not round?
Take the feature's extremes instead of fitting its shape. Run the detector on a coarse image, read the bounding box of the hits, set the center to the box midpoint and the radius to half the box diagonal plus a margin of a few pixels. The mask then covers a rotated or irregular feature with one circle, and the count check still applies with the drawn radius.
Why does my saved mask open with gray edges?
The file was written as a JPEG. Lossy compression stores frequency blocks, not exact pixels, so the sharp 0-to-255 rim becomes a ramp of intermediate grays and the binary mask now contains values like 41 and 214. Re-thresholding that file shifts the boundary by a pixel or two. Write masks as BMP or PNG; both are lossless and keep exactly two values.
Can one mask hold several circles?
Yes. Draw each filled circle with value 255 onto the same zero image; where they overlap the value stays 255, so the union acts as one region. Keep that in mind when regions must be reported separately: either space the centers at least the two radii apart, or build one mask per region and process each against the same image.
How much memory does a mask need at 5 MP?
One byte per pixel for an 8-bit single plane. At 2592 × 1944 pixels that is 5,038,848 bytes, about 5 MB; a 24-bit color frame of the same size is about 15 MB. That is small enough to keep one mask per inspection region, or one per recipe, resident in memory at all times.
Does the mask have to be the same size as the camera image?
For image-space coordinates, yes: matching width and height means mask pixel (x, y) is image pixel (x, y) and no offset arithmetic exists to get wrong. You can also make the mask the size of the ROI alone and treat its (0, 0) as the ROI corner, then add the offset to every result coordinate. The full-size mask is simpler and costs little memory.


