A TDI line-scan camera switched to its area operation mode should grab like an ordinary frame camera: one trigger in, one full frame out. Instead, your GenICam consumer hands back a buffer it interprets as 4640 pixels wide and zero or one line tall, while the camera is quietly delivering 256 lines per buffer. Nothing is wrong with the camera, the frame grabbers chain or the cable: the two ends of the stack disagree about which number is the image height. This guide shows which layer owns the height in area mode, ranks the likely causes in order of probability, and leaves you with a buffer-size rule and a timeout rule that survive every operation-mode change.
What area mode changes on a TDI camera
In native TDI operation a line-scan camera exposes one line at a time: the camera's Height node reads 1, and the frame is assembled by stacking lines downstream. Area operation mode reverses the delivery geometry. The camera hands the stream a fixed block of lines per frame — 256 in the case this guide follows — so downstream code can treat it like an ordinary frame camera while the sensor keeps its TDI integration over the moving part. The mode is chosen with a selector on the camera itself, usually a custom node in the remote device map; on the followed case the selector offered TDI and Area. Specialty and 3D cameras publish custom nodes next to the standard ones in their GenICam feature reference, so that reference is where you confirm the exact selector name before scripting anything.
The trap sits one layer away from the selector: how many lines land in each buffer belongs to the datastream, not to the camera's Height node. Height keeps describing sensor geometry, 1 line. The stream delivers 256. Two layers hold two different heights, and only one of them is the image you actually get.
The symptom: a 4640 × 0 buffer that still holds the whole frame
The fetch call returns without an error, but the image comes back 4640 pixels wide and either zero or one line tall. Dump the raw buffer in a debugger and the data is all there: 4640 × 256, exactly what the camera sent. The delivered image height entry even reads 256. Only the interpretation is wrong, and the two variants tell you which end dropped the number:
- 4640 × 1, with real pixels in that single line — your consumer fell back to the camera's Height node, 1 line on a line-scan camera, because the buffer-info height was missing or zero.
- 4640 × 0 — the producer reported a height of zero and no fallback applied, so the image collapsed before your code saw a row.
Either way, the number your code used is not the number the stream delivered, and no camera setting changes that. The camera is healthy; the buffer geometry is being read from the wrong place.
Which layer owns the height
A GenICam chain has two feature maps. The camera's map, the remote device, describes the sensor: Height, line rate, exposure and the operation-mode selector. The datastream's map, served by the GenTL producer that the frame grabber driver installs, describes buffers: their size, their payload type and, on many producers, a BufferHeight node holding the lines delivered per buffer in area mode. At runtime, the standard GenTL call DSGetBufferInfo reports each delivered buffer's geometry through entries such as BUFFER_INFO_HEIGHT, and consumers that support it take the image height from there.
| Where you look | Node or call | What it reads in the followed case |
|---|---|---|
| Camera map | Height | 1 line — sensor geometry, unaffected by the mode switch |
| Stream map | BufferHeight | 256 lines — the block delivered in area mode |
| Runtime, per buffer | DSGetBufferInfo → BUFFER_INFO_HEIGHT | 256 — the delivered image height |
If a node you expect on the stream side is missing, the GenICam node not existing: diagnose feature access errors guide walks the access checks before you blame the producer.
One caution from the case: BufferHeight kept its value when the operation mode was toggled in the vendor's browser, so a node that looks stale may still hold the right number for the current mode — and the reverse is possible too. The delivered image height per buffer is the only value that is definitionally current. Read it after acquisition starts, per buffer, and treat both nodes as hints.
Likely causes, in order of probability
1. Your code sized its image from the camera's Height. The array or bitmap took width from the buffer and height from Height — 1 — so a delivered 256-line frame collapses or shows one line. Test: read the delivered image height from buffer info right after the fetch and print both numbers. If buffer info says 256 and your image says 1, this is the cause.
2. Dimensions cached before the mode switch. The code read its dimensions at startup, then someone switched OperationMode to Area at the camera, and nothing re-read them. Test: restart the application with the camera already in Area and nothing else changed. If the first session then works, your caching is the bug.
3. The camera never received AcquisitionStart. In the working session on this setup, the camera's AcquisitionStart command was executed before the stream was started, and AcquisitionStop after it stopped. A consumer that starts the stream without commanding the camera delivers no completed buffers, which shows up as empty fetches and event timeouts. Test: grab in Area with the vendor's own GenICam browser; if only that works, compare the command sequences around start and stop.
4. The producer does not fill buffer-info height. Some producers report 0, which is where the consumer's fallback to Height comes from. Test: log the delivered buffer size in bytes and compare with width × delivered height × bytes per pixel — if the byte count matches the full frame while your image is one line, the geometry is available and simply unused.
5. The acquisition timeout is shorter than one frame. On the case setup the event wait was effectively 1 ms against a succeeding session that used an unlimited wait, so every buffer was abandoned before the frame completed. Test: raise the timeout above twice the frame period; the case cleared all event timeouts at 1000 ms.
Causes 1, 2 and 4 are geometry bugs: short or empty images with otherwise clean logs. Causes 3 and 5 are timing bugs: timeouts. Fixing the timing without fixing the geometry just gets you a well-timed wrong image.
The fix: read the stream, then size the buffer
- Put the camera in Area with its operation-mode selector. Confirm the node name in the camera's GenICam feature reference; on the followed case the selector sat in the remote device map with TDI and Area as choices.
- Start acquisition once and, for the first delivered buffer, read width, delivered image height and buffer size from DSGetBufferInfo (BUFFER_INFO_HEIGHT and the adjacent entries of the same call). Read these after the fetch, not before it.
- Allocate every buffer with at least delivered width × delivered height × bytes per pixel. If your code announces its own buffers to the stream instead of letting the producer allocate them, that product is the floor, not a target to aim near.
- Interpret the image with the delivered image height. Width × delivered height × bytes per pixel must equal the delivered buffer size; if it does not, one of the three inputs is stale.
- Repeat steps 2 and 4 after every operation-mode change, and after any change to pixel format or line rate. Never carry dimensions across a mode switch in a variable.
Step 2 is also your permanent assertion. Log delivered width, height and buffer size for the first frames of every session and fail loudly when delivered width × delivered height × bytes per pixel stops matching the allocation. A ring of buffers makes that logging painless — the save an image sequence from a frame grabber with a ring buffer guide shows the structure.
Worked example: sizing the buffer for a 4640-pixel camera delivering 256 lines
Inputs, and where each comes from:
- Width: 4640 px — the line length this camera delivers, read from the same buffer-info call as the height.
- Delivered image height: 256 lines — what BUFFER_INFO_HEIGHT reported once the camera ran in Area.
- Bytes per pixel: 1 B — Mono8, one byte per pixel.
Rule in words: a buffer must hold every pixel of a delivered frame, so its size in bytes is width × delivered height × bytes per pixel.
Arithmetic:
The wrong path, for contrast: sizing from Height gives 4640 px × 1 line × 1 B = 4,640 B — exactly 1/256 of the delivered frame, about 0.4 %, which is why the image arrives as 4640 × 0 or as a single line.
Decision: allocate at least 1,187,840 B per buffer — round up to 1.25 MiB for alignment headroom — and interpret each frame with the delivered height of 256. The threshold is exact, not a rule of thumb: if allocated bytes < delivered width × delivered height × bytes per pixel for any buffer, the geometry is wrong no matter what the camera settings say. Re-run the check after every operation-mode change; the same camera in native TDI mode delivers 1-line buffers and the same formula folds back to 4640 B.
The timeout that hides the geometry error
Two waits sit between the camera and your code. The producer waits for the buffer-complete event; the consumer applies its own acquisition timeout on top. Both must outlast one frame, and one frame is the delivered height divided by the line rate. At an illustrative 20 kHz line rate, 256 lines take 256 ÷ 20,000 lines/s = 12.8 ms per frame, so a timeout below roughly 26 ms can expire mid-frame; the case cleared every event timeout at 1000 ms. The safest floor is twice the frame period, which leaves room for trigger jitter and bus latency — frame timing and how to measure it are covered in camera buffer timestamp latency: interpret frame timing.
One hard rule from the case: do not use a negative sentinel for the timeout. The consumer here threw an overflow error in its event-manager update path when handed -1. Use a positive timeout sized from the frame period, because a finite wait is also what turns a silent hang into a readable error when the camera stops streaming.
Prevention checklist
- Re-read geometry after every mode change. An OperationMode toggle invalidates every cached dimension; re-read delivered width and height from buffer info immediately after the next fetch.
- Assert the allocation at runtime. Check allocated bytes ≥ delivered width × delivered height × bytes per pixel per buffer, inside the acquisition loop, not only in set-up code.
- Log the first frames of every session. Delivered width, height, buffer size and pixel format, one line per frame; the log catches a mode switch that happened outside your code.
- Keep the timeout above twice the frame period, and re-check it whenever line rate or delivered height changes.
- Prove the configuration with the vendor's GenICam browser first. If the vendor tool delivers full 256-line frames while your code does not, the camera and producer are right and the bug is in your program.
- Re-run bring-up after upgrades. A producer update can change what DSGetBufferInfo reports; frame grabber driver mismatch after a vision software upgrade is the same class of producer-side surprise.
- Separate wiring faults from geometry faults. No image at all is a different article: start with camera connected but no image: check the frame grabber cable wiring.
Picking the producer for a TDI camera
TDI installations run on frame grabbers, and the grabber supplies the GenTL producer whose buffer reporting this whole article depends on. Shortlist by interface first in the accessories finder, then keep the producer current, because geometry reporting and acquisition command handling live in its driver. If your inspection does not actually need TDI integration over fast motion, an ordinary model from the area scan cameras category removes the mode switch from the problem entirely.
For a CoaXPress camera, an octo CXP-6 frame grabber such as the MV-GX1008 anchors the chain:
- Interface
- CoaXPress
For a Camera Link camera:
- Interface
- Camera Link
Compatibility rules for line-scan cameras on Camera Link grabbers deserve their own reading: see Camera Link frame grabber compatibility for line scan, and the signaling-level cousin of this problem, LVDS line scan camera to frame grabber: match the signaling first. Frame geometry going wrong in the other direction — lines landing across the wrong boundary — is covered in line scan image overlap across frame boundaries.
If you are speccing the chain from scratch, the configurator's extras step matches grabbers to the camera interface you have picked, and the systems topic page collects the acquisition-side fixes around this one.
Questions
How do I tell whether a GenICam feature belongs to the camera or to the datastream?
Open the GenICam browser that ships with your frame grabber driver: the node tree splits into a remote device branch, which holds camera features such as Height, line rate and the operation-mode selector, and a data streams branch, which holds stream features such as BufferHeight and buffer size. Features describing buffers live on the stream side; features describing the sensor live on the camera side. The GenICam feature reference for your camera lists which nodes are custom.
Can I just write a larger value into the camera's Height node?
No. On a line-scan TDI camera Height describes the sensor's line geometry and reads 1 line, and writing an area-mode value into it does not change what the stream delivers. You would allocate for a frame that never arrives. Switch the operation mode with the camera's own selector, start acquisition, and read the delivered image height from the buffer info instead — that is the only number your allocation has to match.
What buffer count and timeout should I start with?
Keep at least three buffers in the ring so one can be processed while others fill, and set the acquisition timeout to at least twice the frame period: frame period is delivered height divided by line rate, so 256 lines at an illustrative 20 kHz needs 12.8 ms per frame, and a 30-50 ms timeout gives comfortable margin. On the setup followed in this guide, a 1000 ms timeout cleared every event-timeout error.
Which pixel formats change the buffer-size arithmetic?
Any format that is not one byte per pixel. Mono8 packs one byte per pixel, so 4640 × 256 needs 1,187,840 bytes; 8-bit RGB needs three bytes per pixel, 1,187,840 × 3 = 3,563,520 bytes. Mono10 and Mono12 are commonly two bytes per pixel unpacked, 2,375,680 bytes, while packed variants are denser and producer-specific — read the bytes per pixel from the payload information your producer reports rather than assuming.
Why did setting a negative timeout crash the consumer?
A GenTL event timeout is a duration in milliseconds, and in the case followed the consumer's event-manager update path overflowed when handed a -1 sentinel. Infinite-wait behavior, where a producer supports it at all, is exposed differently by each consumer library, so always set a positive timeout sized from the frame period with margin.

