Cameras & sensorsExplainer

Camera float values are silently snapped to the nearest increment

Set 16 µs, get 12 µs: the camera snapped your float value to the nearest valid increment, no error raised. The rounding rule, readback calls and timing math.

LumaSpec Vision9 min read Share on LinkedIn

On this page 11 sections
  1. 01What the camera does instead of raising an error
  2. 02The snap rule, as a formula
  3. 03A worked example: 16 µs in, 12 µs out
  4. 04Where the silent snap costs you
  5. 05The set-readback-verify pattern
  6. 06Find min, max and increment without writing code
  7. 07Choose the rung on purpose
  8. 08When the readback is still not the exposure you get
  9. 09Checks that keep the snap from lying to you
  10. 10Shortlist the hardware, then wire the pattern in
  11. 11Questions

You set the exposure time to 16 µs, the call returns without an error, and the first frames come back darker than your light budget promised. Nothing crashed: the camera took your float value and snapped it to the nearest value it can physically produce, and most GenICam libraries do that quietly. If you compute strobe delays, blur budgets or brightness targets from the number you typed, your math is running on a value the camera does not hold. The rest of this page gives you the rounding rule as a formula, a worked example you can check by hand, and a set-readback pattern that keeps timing math on the camera's real numbers.

What the camera does instead of raising an error

Every GenICam float feature — exposure time, gain, timing delays — carries three numbers alongside its value: a minimum, a maximum and an increment. The camera can only hold values on the rungs between those limits, spaced one increment apart. When you set a value between rungs, the library picks the nearest rung and writes that instead. In most wrappers there is no exception, no error return and no log line.

The behavior surprises engineers who are used to enumerations, where an invalid symbol raises an error you can catch — see GenICam enumeration invalid value: check symbolics before you set. Floats take the opposite deal: your value is accepted, corrected and never mentioned again. Readback surprises appear in non-float nodes too; camera trigger mode source readback mismatches is the trigger-mode cousin of this problem.

The snap rule, as a formula

Name the node's minimum vmin, its increment inc, and the value you requested v. The writable values are the rungs min + k × inc for k = 0, 1, 2 and so on up to the maximum, and the library selects the rung closest to v:

v(k) = vmin + k × inc k = 0, 1, 2, … v* = vmin + round((v − vmin) / inc) × inc

Three details matter. The minimum is itself a rung, so k = 0 is always legal even for a tiny request. A request exactly halfway between two rungs is a corner case decided by the library's rounding rule, so keep setpoints off the midpoints. And a few float features report no constant increment at all — the standard's HasInc returns false — in which case there are no rungs to land between.

The GenICam float interface defines the increment as a queryable property; the standard names the calls HasInc and GetInc. The rung size is never a mystery. The next two sections show where to read it and what it does to a real request.

A worked example: 16 µs in, 12 µs out

Take a camera whose exposure time node reports a 12 µs minimum and a 9 µs increment. Its rungs sit at 12, 21, 30, 39 µs and upward. You request 16 µs:

  • (16 − 12) / 9 = 0.44, which rounds to k = 0, so v* = the bottom rung — a quarter less exposure than you asked for, with no error raised.

The table shows where nearby requests land on this node:

You request The camera holds Why
16 µs 12 µs 4 µs down vs 5 µs up
18 µs 21 µs 6 µs down vs 3 µs up
21 µs 21 µs exact rung
25 µs 21 µs 4 µs down vs 5 µs up
26 µs 30 µs 5 µs down vs 4 µs up
30 µs 30 µs exact rung

Just past the midpoint the snap flips direction, which is why 25 and 26 land on different rungs.

Where the silent snap costs you

Signal strength is proportional to exposure time, so the down-snap in the example costs about a quarter of the signal — log₂ of the request-to-actual ratio is roughly 0.4 stops. If your light budget carried 20% headroom, one set call consumed it.

Motion blur moves with it. A part traveling 100 mm/s smears 100 mm/s × 16 µs = 1.6 µm during the exposure you asked for; the snapped value gives 1.2 µm and an up-snap to the next rung gives 2.1 µm. A published blur budget of 1.5 µm then passes on one camera model and fails on another, with identical code on both.

Timing sequences suffer in the other direction: a frame period or strobe delay computed as exposure plus margin loses that margin to an up-snap, and on triggered lines that shows up as overtrigger faults at production rate — sequenced exposure trigger timing: avoid overtrigger errors walks that failure down. Multi-camera banks drift unevenly: two models asked for the same setting can hold different actual values when their increments differ, so read back per camera, per feature.

Every area scan camera you drive through a GenICam float node follows this rule, whether it streams over GigE or USB3:

Area scan camerasHikrobot MV-CU020-90GC 2.5 MP Area Scan Camera, GigE, GMAX4002, ColorMV-CU020-90GC
Sensor format
1/1.7"
Lens mount
C-mount
$309.00View part
Area scan cameras12 MP USB3 area scan camera, IMX226, 1/1.7" rolling shutter, 30 fps, 4000 × 3000 resolution (LS-CMR-0166)LS-CMR-0166
Sensor format
1/1.7"
Interface
USB3
$560.00View part

On high-speed lines the exposure sits inside the cycle budget itself, so the same snap moves line rate as well as brightness.

Area scan camerasHikrobot MV-CH120-11UC 12 MP Area Scan Camera, USB3, IMX304, High-speed version, ColorMV-CH120-11UC
Sensor format
1.1"
Lens mount
C-mount
Interface
USB3
Resolution
12 MP
$2,339.00 · quotedView and quoteBuild a system with it

The quiet rounding waits downstream too. A setpoint that leaves your inspection code correct can shift again inside the controller's own float math; if your results drive PLC logic, this PLC float-rounding procedure shows where one controller family rounds, so the number you send is the number that arrives.

The set-readback-verify pattern

The fix is mechanical and costs one line of code: after every float set, read the value back and compute from the readback, not from the request.

// set - readback - verify, in pseudocode
double requested = 16.0;                    // µs — what you intend
double applied   = 0.0;                     // µs — what the camera holds
exposure.SetValue(requested);               // the node may snap silently
applied = exposure.GetValue();              // read it back, always
double increment = exposure.GetIncrement(); // rung width, for computing exact rungs

SetValue, GetValue and GetIncrement are the common wrapper spellings for the three calls; your library's names may differ, but the order does not: set, read back, then compute. On the example node, the readback prints 12.0 for a request of 16.0 — that one mismatched line is the snap, caught. In production, log requested and applied for every float feature at startup; the diff list is your camera's real timing specification.

The same discipline applies while the line runs: setting and reading parameters inside a capture loop has ordering traps of its own, covered in camera parameter set and get in a capture loop safely, and the Python side of the set call itself is walked through in setting camera exposure time through a GenICam API in Python.

Find min, max and increment without writing code

Open the camera in a GenICam feature browser and expand the exposure node: the value control sits beside its minimum, maximum and increment, and most viewers repeat the numbers in the node tooltip. In code, a capabilities query returns the same fields per feature — how to query camera capabilities with GenICam shows the calls.

Read the three numbers at startup, per camera. They belong to that feature on that model, not to the interface, so one camera's 9 µs rung predicts nothing about the next camera on the line. Recording them in the project notes takes a minute and settles most timing arguments later.

Choose the rung on purpose

When the exposure you need falls between rungs, do not request it and hope. Compute k yourself and set an exact rung. On the example node, a blur budget that allows at most 16 µs needs k = floor(0.44) = 0, so set the bottom rung: inside the budget, a quarter dimmer. A signal floor at the same level needs k = ceil(0.44) = 1, so set the next rung up: brighter, more blur. State the rounding direction in the comment beside the set call, because the trade is an engineering decision, not a rounding accident.

If you change exposure while the camera is already streaming, the mid-acquisition path adds its own failure mode; reduce exposure time mid-acquisition without an overtrigger error shows the safe sequence.

When the readback is still not the exposure you get

Two cases leave the readback truthful but not final. Under automatic exposure the camera re-times each frame and your manual value is only a starting point; the applied time then arrives in each frame's metadata, and read exposure time from frame metadata under auto exposure shows how to pull it. On triggered cameras, when the exposure starts relative to your trigger call is a separate question from how long it lasts — trigger call versus exposure time: understand capture timing separates the two. Snap correction and trigger timing are independent effects; check both before blaming either.

Checks that keep the snap from lying to you

  • Set the value, then read it back with GetValue and compare before you use it anywhere.
  • Read minimum, maximum and increment for every float feature you set, at initialization, per camera.
  • Compute strobe delays, frame periods and blur budgets from the readback, never from the request.
  • Keep setpoints off exact midpoints between rungs.
  • Under auto exposure, take the applied exposure from frame metadata instead of trusting the node.
  • Set exposure before streaming starts; the initialization order decides when your readback is trustworthy, as set manual exposure before streaming: initialization order explains.
  • Save validated settings to a user set on the camera and load it at startup, so a power cycle cannot trade your rungs for defaults.

Shortlist the hardware, then wire the pattern in

If you are specifying cameras for a timing-sensitive cell, shortlist by sensor, resolution and interface in the area scan camera finder, then check the feature browser on the exact model before the pilot run — the rungs you find there are the ones your code will live with. The camera configurator pairs camera, lens and light in one pass, the area scan cameras category lists every model in the range, and more camera-side fixes sit on the cameras topic page. When a station needs this level of timing scrutiny and you want a second pair of eyes on the plan, the contact page reaches the applications team.

CamerasArea scan cameras

GigE, USB3, 10GigE and CoaXPress

1,287 models · from $149.00

Questions

Do all float features snap, or only exposure time?

Every float node that carries an increment snaps: exposure time, gain, and timing features such as strobe or flash delay. Exposure time is where you notice it, because image brightness scales linearly with exposure, so a snap of a few microseconds on a short setting is a double-digit percentage signal change. A small gain step, by contrast, is easy to miss on a histogram. The habit is identical for every feature: set, read back, compute from the readback.

How do I see min, max and increment without writing code?

Yes — every vendor SDK ships a GenICam browser of some kind. Select the exposure feature and its minimum, maximum and increment appear alongside the value, usually in the tooltip as well. The same three fields come back from a capabilities query if you would rather script it. Write the numbers into the project notes once per camera model; the rungs belong to that feature on that model.

What does the GenICam standard say should happen when a float is rounded?

The GenICam control protocol treats a float that needs rounding as a warning case: the rounded value is set, and a warning can travel back to the host. In practice most libraries apply the nearest value quietly and surface nothing, which is why the behavior reads as silence. Treat the readback as the authoritative confirmation instead of waiting for an exception that will not come.

Does the snapped value survive a power cycle?

No better than any other feature value. Camera settings live in working memory, and unless you store them to a user set and load it at startup, the camera returns to its power-on defaults. A line that was verified on one rung can boot onto a different one tomorrow from a saved set — one more reason the startup readback log is worth keeping.