System designHow-to

Machine vision overlay line thickness: draw it yourself

Why machine vision ROI contours render at a fixed width, and how to draw overlay lines of any pixel thickness yourself, then burn them into exported images.

LumaSpec Vision11 min read Share on LinkedIn

On this page 10 sections
  1. 01Why the contour draws at one pixel
  2. 02Pick the layer for the job
  3. 03What you need
  4. 04How thick: the scale rule
  5. 05Worked example: a 2448 × 2048 frame in a 960 px wide pane
  6. 06Draw the stroke at width w
  7. 07Check the result
  8. 08Common mistakes
  9. 09When a thicker line is the wrong answer
  10. 10Questions

An ROI contour is handy right up to the moment you need to see it from across the cell. The outline your vision software draws around a found part renders at a fixed one-pixel width, with no pen setting behind it, so the contour that looks crisp at 1:1 becomes a faint whisker on a scaled-down display window or in an exported report image. This guide explains why the width is fixed, gives the arithmetic that sets the stroke for your window, and walks through drawing overlay lines of any thickness and burning them into the images you archive. It applies wherever your code owns an image buffer: a vision controller driving a display, or a smart camera whose results you annotate before export.

Why the contour draws at one pixel

An ROI contour is not pixels. It is a shape descriptor — corner points, a rectangle, an ellipse, a list of line segments — attached to the image in memory, and the display control re-renders that descriptor on every refresh. Its renderer strokes outlines one image pixel wide and accepts no pen input, so there is nothing to tune there. Vision libraries state the same contract for their overlay primitives: they annotate without touching the pixel data, and they take shapes and colors, not pen styles.

Two layers matter, and width control lives in exactly one:

  • Annotation layer. Overlays drawn over the image without modifying pixel data: the ROI contour you place while tuning, plus any primitives your code adds. Reversible, invisible to your measurement tools, no width control on the contour.
  • Pixel layer. A merge step — the IMAQ Merge Overlay call in one vision API is the pattern — copies annotation into the buffer so the shape becomes real pixels that travel inside the saved file. After a merge, every downstream tool sees the line as image content.

Because the merge writes pixels, whatever your code can draw, it can draw at any width. Some libraries' basic draw calls offer only a few shape options and no pen-thickness input at all, which is why the steps below include a repeat-and-offset fallback. If your overlays do not render at all, that is a different fault — the guide on drawing the results on the image covers overlays that never appear.

Pick the layer for the job

What the image is for Where the line lives Width control
Live operator guidance Overlay drawn by your code Any width, with a pen primitive
Exported or archived evidence image Merge on a copy, after inspection Any width; you own the pixels
ROI while tuning the tool Contour annotation Fixed 1 px — leave it
Frame another inspection reads later Overlay only, never a merge None — keep the buffer clean

The table is the decision; everything after it is execution.

What you need

  • The contour geometry as numbers your code already holds: a rectangle's two corners, a rotated rectangle's four corners, a line's two end points, an ellipse's center and radii. Read the descriptor; do not trace the screen.
  • A buffer your code owns, 8-bit mono or color, at the acquired frame's pixel dimensions.
  • A drawing primitive that accepts a width: a pen property in the Windows graphics classes (System.Drawing.Pen.Width is the API name) or a picture-draw function whose pen cluster carries a line-size element, as Draw Round Rect.vi does.
  • A fallback for libraries with no width input: a one-pixel draw call you can repeat, plus integer offset arithmetic.
  • A display window whose size you read back in pixels; the guide on controlling display window size and screen position shows how to pin it.

Keep the descriptor handling consistent with your ROI design for responsive display tools, and one routine serves tuning and runtime.

The steps run wherever that buffer lives, on a vision controller or a smart camera. Both controllers below buffer full frames, drive a local display and carry four built-in light channels, so the drawing code and the strobe timing share one box; the first offers three to four camera LAN ports, the second five or more and includes the VM6200Pro license.

Vision controllersHikrobot MV-VC2140-128G40-1T VC2100 Vision Controller, N97, 8GB+128G SSD+ 1THDDMV-VC2140-128G40-1T
LAN ports
3-4 LAN ports
USB 3 ports
2 USB 3 ports
$1,679.00View part
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
$2,419.00View part

How thick: the scale rule

The display maps image pixels to window pixels at scale s, so a one-image-pixel stroke lands on s screen pixels. Below one screen pixel the stroke drops out or flickers; two to three screen pixels reads as a deliberate mark at operator distance.

s = window width (px) / image width (px) minimum stroke = ceil(1 / s) image px readable stroke = ceil(3 / s) image px

The rule assumes uniform scaling, so lock the aspect ratio. At 1:1, s = 1 and a one-pixel stroke is already solid. What moves the number: the window size first — a resizable pane re-runs the rule every time the operator drags it — and multi-view layouts, where each grid cell scales the frame on its own.

Worked example: a 2448 × 2048 frame in a 960 px wide pane

Inputs: the buffer is 2448 px wide by 2048 px tall — the frame size a 5 MP mono camera hands you, read from your own image header; the pane the operator watches is 960 px wide; the part's bounding box is 300 px by 220 px.

Scale: s = 960 / 2448 = 0.392.

Minimum stroke: ceil(1 / 0.392) = ceil(2.55) = 3 image px, which renders as 3 × 0.392 ≈ 1.2 screen px — solid, never zero.

Readable stroke: ceil(3 / 0.392) = ceil(7.65) = 8 image px, rendering 8 × 0.392 ≈ 3.1 screen px.

Merged cost: the contour follows the bounding box, so the path runs 2 × (300 + 220) = 1040 px and the stroke overwrites 1040 × 8 = 8320 px, which is 8320 / (2448 × 2048) = 8320 / 5013504 ≈ 0.17 % of the buffer.

Decision: draw 3 px when the line only has to be findable, 8 px when the operator must read the shape at a glance, and merge on the export copy only — 0.17 % of the pixels is nothing in a report and everything in a re-inspection.

Draw the stroke at width w

  1. Copy first. Clone the acquired frame into a display buffer at full image resolution. The buffer your measurement tools read stays untouched for the whole cycle.
  2. Convert the contour to coordinates. Pull the shape from the ROI descriptor — rectangle corners, rotated-rectangle corners, line end points — in image pixel coordinates, never window coordinates.
  3. Set the pen and draw. Set the width to w image px (System.Drawing.Pen.Width in the Windows graphics classes, or the line-size element of a pen cluster) and stroke each segment. Turn anti-aliasing off for anything that will be merged.
  4. No width input? Offset and repeat. Draw the same shape N times, each copy shifted 1 px: N nested rectangles from corners offset by k px inward, or N parallel lines 1 px apart across the stroke. N passes give an N-px stroke; the 8-px example above is eight passes.
  5. Or dilate. Draw the one-pixel contour, then run a k × k maximum filter over the buffer with odd k: 3 × 3 turns a 1-px line into 3 px, 5 × 5 into 5 px. Corners fatten too — fine for annotation, wrong where the stroke must hug a measured edge.
  6. Merge last, on the copy. Once inspection has returned, apply the merge so the annotation becomes pixels in the export copy. The analysis buffer never sees it.

Check the result

  • Resize the pane to its smallest normal size, take a screenshot, and count screen pixels across the stroke: at s = 0.392 with w = 3 px you expect about 1.2 — the test is that the count never reaches zero.
  • Zoom to 1:1 and check that the stroke hugs the contour. Half of w sits inside the shape, so an 8-px line intrudes 4 px into the part; keep annotation off measurement regions that tight.
  • Run one cycle with the overlay suppressed and one with it drawn. Results must be identical, because the measurement buffer never changed.
  • Reopen the exported file and measure the stroke: it must equal w image px, with no anti-aliasing fringes.
  • On mono buffers, pick the gray value further from the part: a merged 255 stroke vanishes on a bright part, 0 vanishes on a dark one.

Archives that show several results side by side scale each image on its own, as in the OCR results visualization layout; apply the rule to the grid cell width, not the full frame — a 480-px cell on the same 2448-px frame halves s to 0.196 and pushes the minimum stroke to 6 px.

Common mistakes

  • Merging before measuring. The stroke has already replaced part pixels, so defects under it are gone and its edges can split a blob count. The merge is the last operation of the cycle, on the copy.
  • Pen width in the wrong units. Graphics pens size in drawing-surface units. Draw a 1-px stroke at 1:1 and read the pixel values on both sides of it: if the transition spans two pixels, your pen unit is not the image pixel.
  • Anti-aliased strokes in evidence images. Anti-aliasing fills the stroke border with intermediate grays, so a nominal 2-px line becomes about 4 px of gray levels on a mono buffer, and those grays can fall inside a threshold band. Integer widths, aliasing off.
  • Drawing into the window-sized bitmap. Draw at screen scale and the exported full-resolution image keeps the thin original. Draw into the image-sized buffer and let the display scale it up.
  • Thick lines as a fix for a detection problem. The contour you draw is presentation; the line your code detects is measurement. If Hough line detection runs slow, that is a processing problem no pen width will fix.

When a thicker line is the wrong answer

While tuning, leave the contour at one pixel: a fat outline covers the very transition you are judging. Keep every overlay non-destructive when the frame continues downstream — a merged stroke across a code kills reads for the same reason the guide on preserving pixels for reliable barcode reads gives: the decoder needs the module pixels, not your pen. Labels follow the same logic: keep them to one short line as in displaying region width and height in one string, and size the font in image pixels with the same scale rule.

For inspections that run on the camera itself, the on-device results view is firmware you do not restyle; the pixels you control are the ones your own code writes before the image leaves for the HMI or the archive. That is the smart camera case, and the smart camera finder filters the range by resolution, interface and shutter:

Smart cameras & vision sensorsHikrobot MV-SC5060XM-08M-WBN SC5000X series Smart Camera, 6 MP, Mono, 8 mm focal length, M12-mountMV-SC5060XM-08M-WBN
Interface
GigE
Resolution
6-10 MP
Sensor format
1/2.4"
Shutter
Rolling
$1,629.00View part

The MV-SC5060XM-08M-WBN pairs a 6 MP mono sensor with an 8 mm M12 lens on GigE — the class of camera where this burn-in routine is the annotation you control.

If you are sizing a station around this workflow, the quote request takes your frame size, smallest pane width and camera count, and we come back with a controller matched to those numbers; for the display side of the cell, an industrial mini PC can drive the HMI while the controller handles the cameras.

Questions

Can I set the pen size on the ROI tools themselves?

No. The contour is annotation metadata the display control renders, and its renderer strokes a fixed one-pixel outline with no pen input; the same applies to the overlay an ROI tool places. Thickness control starts where your own code writes pixels, through a pen-width primitive, repeated one-pixel passes, or a dilation of a one-pixel stroke.

Does merging the overlay change my inspection results?

It can, if you merge before measuring. The merge writes the annotation values into the buffer, so a defect under the stroke is covered and the stroke's edges can appear as false features. Inspect first, merge on a separate copy for export, and keep overlays non-destructive while the pipeline runs.

How thick should an overlay line be when the image is zoomed out?

Divide the window width in pixels by the image width in pixels and set the stroke to the reciprocal, rounded up. At half scale that is 2 image px, at a third of scale 3 px. For text-like readability aim for two to three screen pixels and multiply the minimum by the same factor.

Can I draw a thicker line without covering the feature I marked?

Offset the path outward by half the stroke width, w / 2, so the stroke's inner edge sits on the original contour. At 8 px that moves the visible edge 4 px away from the feature, and any region measured at that tolerance should be annotated on the export copy only.

Does the same rule apply to overlay text?

Yes. Pick the font height in image pixels with the same scale rule: a caption that must stand 20 screen pixels tall in a 0.4-scale view needs a 50-px font in the image, because 20 / 0.4 = 50. Draw text after the shapes, and remember a merge burns the characters into the buffer exactly like a line.