System designTroubleshooting

Latest image only returns duplicate frames: drain the buffer queue

Why a latest image only grab returns duplicate frames, how stale a 40 fps free-run queue gets, and whether more buffers, a drain step or a trigger fixes it.

LumaSpec Vision12 min read Share on LinkedIn

On this page 10 sections
  1. 01Why does latest-image-only still return the same frame?
  2. 02How stale is the frame my grab actually returns?
  3. 03Why did the same code behave differently in C++ and Python?
  4. 04Does a software trigger really cost 100 ms per shot?
  5. 05When should I stop free-running and trigger from hardware?
  6. 06How do I keep free-run frames fresh without a trigger?
  7. 07How do I prove the fix before it reaches production?
  8. 08Which fix fits which station?
  9. 09What do I need for a stop-and-shoot station?
  10. 10Questions

You mount the camera on a motion controller, drive from station A to B to C, capture at each stop — and the same picture comes back three times, even though you asked for the latest image only. The grab strategy is almost never the culprit; the buffer queue behind it is. With one buffer and one retrieve per step, the newest frame never reaches you. Here you will compute how stale a free-running queue gets, see why identical logic behaved differently in two language bindings, and choose among the three fixes: a bigger buffer pool with a drain step, a throttled frame rate, or a hardware trigger per position. Everything applies to any of the area scan cameras that stream over GigE or USB3.

Why does latest-image-only still return the same frame?

Because the strategy only selects the newest frame already in the queue: with the buffer count set to 1 and the retrieve called once per step, the lone buffer is full, new frames have nowhere to land, and every fetch returns the first frame the stream delivered.

The mechanism is worth a walk-through. Every frame the camera delivers needs a free buffer to land in. The retrieve call takes a frame out, and that buffer returns to the pool only when you release the grab result — by reassigning the pointer, resetting it, or letting it leave scope. Set the pool to a single buffer, fetch once per step, and hold the result in a member between calls: the pool is empty from the first frame onward. The camera has nowhere to put frame 2, frame 3, or any frame after, so every fetch hands back buffer 1 and its station-A pixels.

A free-running camera deepens the trap, because frames keep arriving every frame period whether you fetch or not. The fix is room and turnover: raise the buffer count — MaxNumBuffer in GenICam parlance — to the SDK default (commonly 10) or set it to 10 explicitly, release every grab result promptly, and let the latest-image-only strategy work on a queue that actually cycles.

How stale is the frame my grab actually returns?

With a cycling queue the returned frame is at most one frame period old — 25 ms at a 40 fps free-run — while a stalled one-buffer queue returns a frame of unbounded age.

frame period T = 1 / frame rate frames delivered during a settle = settle time / T frame age at retrieval with a cycling queue ≤ T

Worked example, with these example inputs: a 40 fps free-run, a 200 ms settle between move and capture, stations 200 mm apart, and a stage speed of 100 mm/s.

  • Frame period: T = 1 / 40 fps = 0.025 s = 25 ms.
  • Frames delivered while the stage settles: 200 ms / 25 ms = 8 frames per step.
  • Cycling pool with a drain-and-keep fetch: the kept frame was exposed after the drain, so its age is at most one period, 25 ms — well inside the settle, showing the stage parked at the new station.
  • Stalled one-buffer pool: the fetch at station B hands back the frame exposed at station A, 200 mm away, and the verdict describes a part the stage has already left.

One condition carries all of it: the settle must be longer than one frame period plus the exposure. Nothing here survives a settle shorter than the frame period, which is why a throttled camera needs its own arithmetic.

While the stage travels, consecutive free-run frames sit 100 mm/s × 0.025 s = 2.5 mm apart in stage travel, so the stream overlaps itself heavily. That is normal and harmless — as long as you never inspect a frame whose identifier did not change after the settle.

Why did the same code behave differently in C++ and Python?

The language did not matter — the retrieval cadence did, because the continuous fetch loop kept every buffer cycling and latest-image-only always had a fresh frame to hand over, while the once-per-step path with one buffer stalled at the first delivered frame.

The binding was not the variable; the ownership discipline was. The Python function retrieved a result, converted it, released it and returned, so every buffer went back into the pool each cycle. The C++ path fetched once per move-and-settle and kept its grab result alive in a member between captures. A result you hold is a buffer the pool cannot reuse, so a one-buffer stream stalls at the first frame and stays stalled.

The prevention rule is short: release every grab result as soon as the pixels are copied, and never hold one across a move. The same discipline decides neighboring failures — torn pixels when a callback buffer is reused under you, and leaks when results pile up — which is why copying the buffer before you queue it and managing image buffer ownership each get their own treatment.

Does a software trigger really cost 100 ms per shot?

With the camera left open and configured, a software trigger is sampled at the next frame start, so the wait is at most one frame period plus exposure — about 25 ms at 40 fps — and a 100-120 ms delay points at reconfiguration work per shot, not the trigger.

The remainder, roughly 75-95 ms, is a one-shot path that stops the stream, reapplies settings, grabs one frame and tears down again: every one of those steps is a control-link transaction, not optics. Configure software triggering once, leave the stream running, and fire the trigger per station; the frame arrives within about one frame period plus exposure, which at 40 fps beats the 200 ms settle you were already spending.

Holding a camera open between shots is its own small discipline. Keeping a camera open and ready between triggered grabs covers the session pattern, why one-shot capture misses frames covers the timeout side, and if your loop starts and stops acquisition per shot, why repeated starts need a stop explains the hang on the second start.

When should I stop free-running and trigger from hardware?

Trigger from hardware the moment each image must belong to one known stage position: wire the controller's motion-complete output, or a photoelectric sensor at the parked station, into the camera trigger input and the frame is exposed after the settle by construction.

Choose the trigger when the image has to be evidence: gauging, audit trails, any station where someone downstream asks which part produced which frame. Fire a short pulse only after the settle has elapsed, configure the trigger source and edge once, and the camera exposes exactly once per pulse — idle frames between stations also disappear from the link.

Two failure modes live here. The trigger configuration belongs to the camera's acquisition block, and a free-run default silently ignores your pulses — the per-camera setup is walked through in hardware trigger configuration per camera. A trigger that fires once and never again usually means the acquisition mode needs re-arming between shots, the pattern in hardware trigger stops after one frame.

How do I keep free-run frames fresh without a trigger?

Keep the buffer count at 2 or more and, after each settle, retrieve once to drain and once to keep: the kept frame was exposed after the drain, so its age is about one frame period, 25 ms at a 40 fps free-run.

The drain-and-keep wait is bounded by that period: worst case you wait a full period for the post-drain frame, half a period on average, which undercuts any one-shot reopen path. Two pool slots are enough; ten is more comfortable and costs nothing at these rates.

Throttling buys load, not freshness. Setting the camera's frame rate below its free-run maximum shrinks the link and CPU load between stations, and it makes a plain single fetch safe: keep the throttled period at or below the settle time, and at or below half the settle for margin. For a 200 ms settle that means 5 fps at the limit, or 10 fps (a 100 ms period) with margin. What throttling cannot fix is a slow loop: when frames pile up because processing lags rather than because captures are rare, the diagnosis moves to the acquisition faster than processing pattern.

How do I prove the fix before it reaches production?

Log the per-buffer frame identifier or timestamp with every retrieved frame and assert that it changes: each delivered frame carries an incrementing identifier, so two captures with the same identifier are literally the same buffer. A static scene can look identical; the identifier cannot lie.

Then run the four-step check:

  1. Log the frame identifier with every capture and run stations A, B and C. Pass means three distinct, increasing identifiers.
  2. Cap the pool at one buffer and re-run. Identifiers repeat and the pixels compare bitwise-equal — the symptom, reproduced on demand.
  3. Restore ten buffers, add the drain-and-keep fetch, and re-run. Identifiers climb by roughly eight per step, because the drained frames advanced the counter, and every kept frame is younger than one frame period.
  4. Park the stage and capture twice, one period apart. Distinct identifiers with near-identical pixels mean a live stream watching a static scene, which is what a healthy free-run should produce.

The eight-per-step figure is not a coincidence: it is the 8 frames per settle the worked example predicted, now read straight off the counter.

Which fix fits which station?

Match the fix to what each station must prove: free-run with a drain step when the cycle time is tight, a hardware trigger when the image must trace to one position, and a full continuous queue only when you need every frame.

Station case Fix Frame age Added per shot
Park-and-inspect, tight cycle Pool of 2 or more, drain-and-keep At most one frame period (25 ms at 40 fps) At most one period
Image must trace to a position Hardware trigger after the settle Exposed after the settle by construction Pulse wiring only
Every frame needed in motion Continuous grab, deep pool Every frame kept Full link data rate
Several cameras per host Same pool rules per camera, staggered captures Per camera Link and CPU headroom

Free-run is not automatically stale, and duplicate-looking frames are not automatically duplicates. With a cycling pool and a settle longer than one frame period, the drain-and-keep fetch returns a frame exposed entirely inside the settle, and no trigger wiring is needed. Conversely, a parked stage watching the same part produces near-identical exposures shot after shot, so the identifier log and the bitwise comparison — not the picture — decide whether a repeat is a bug or a boring scene.

When several cameras share one host and the symptom is dropped or desynchronized frames rather than stale ones, the diagnosis moves off the queue and onto the link: recovering an out-of-sync image stream is the companion read for that case.

What do I need for a stop-and-shoot station?

A stop-and-shoot station needs three parts chosen as a set: a global-shutter camera that accepts an external trigger, a clean link into the host, and a controller that runs the grab loop without starving it.

For the camera, the class most park-and-inspect stations use is a 2.5 MP GigE model with global shutter and a C-mount, such as the MV-CU020-90GC, because global shutter keeps the frame intact while the stage decelerates into position.

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
Interface
GigE
Resolution
Up to 3 MP
$309.00View partBuild a system with it

On the link side, a dedicated dual-port PoE network card such as the MV-GE2002P keeps camera streams off the management port and powers PoE cameras over the same cable. The frame grabbers range spans one-, two- and four-port cards for multi-camera hosts.

Frame grabbersHikrobot MV-GE2002P Dual-port PoE NIC, full height V2.0MV-GE2002P
Interface
GigE
Ports
2 ports
$249.00View partBuild a system with it

Where the loop runs, a controller such as the MV-VC5525-256G70-NN puts PoE camera ports and 8 built-in light channels in the same chassis that executes the grab code. The vision controllers range runs from compact low-power units to machines with 5+ LAN ports.

Vision controllersHikrobot MV-VC5525-256G70-NN VC5000 Vision Controller, 12th-I5, 32GB+256G SSD, Supported PCIEMV-VC5525-256G70-NN
LAN ports
5+ LAN ports
USB 3 ports
3-4 USB 3 ports
PoE
PoE camera ports
Light outputs
8 built-in channels
$4,479.00 · quotedView and quoteBuild a system with it

If you are specifying now, the camera finder filters by interface, shutter and resolution, and the configurator walks the chain from sensor to light. More queue, trigger and grab-loop patterns sit on the systems topic page, and the contact page is where a specific station gets a specific answer — include the free-run rate, the settle time and the station count.

Questions

How high should the buffer count go?

Ten is a common SDK default and is plenty for stop-and-shoot work: the queue only needs room for frames that arrive while you are busy. Higher counts add drain time after a halt — ten buffers at 40 fps hold 250 ms of history — so restart the stream or drain it after any pause. The one value that always fails is 1 with a latest-image-only strategy.

Does latest-image-only drop frames on purpose?

Yes: at retrieval it discards everything older than the newest frame, which is exactly right for one inspection per position and exactly wrong for recording. If you must keep every frame while the stage moves, grab continuously into your own queue and size the link for the full data rate, because a 40 fps stream produces 40 exposures per second whether you read them or not.

How do I tell a true duplicate from a genuinely static scene?

Bitwise-compare two captures taken one frame period apart with the stage parked. Two live exposures of an unmoving part differ by a few sensor-noise counts in most pixels; one buffer served twice compares equal to the last bit. Pair that test with the frame identifier log and a stale queue separates from a boring scene in a single run, with no optics touched.

What should fire the trigger on a stop-and-shoot stage?

A plain motion-complete output from the controller, or a photoelectric sensor that sees the part in the parked position, either one giving a short pulse into the camera trigger input. Fire it after the settle has elapsed, leave the trigger delay at zero, and log the camera's trigger counter beside the frame identifier so every stored image traces back to one pulse.

Does a faster camera interface fix the staleness?

No. The stale frame sits in the host-side buffer queue, so a faster link only delivers the same old pixels sooner. Bandwidth matters for a different failure: when the free-run data rate exceeds the link for long stretches, frames are lost before they ever queue up, and a throttled frame rate or a per-shot trigger is the fix for that problem.