You grab a 16-bit frame from a camera that ran all day at 8 bits, and the array in your code has half the elements it should: 153,600 instead of 307,200 for a 640 × 480 image. On a 3D camera the point cloud comes back with a quarter of its values. Nothing is wrong with the camera, the cable or the exposure. The payload arrived complete, and the array was built from it with a crossed count-and-dtype pair. This guide shows the one division that names the defect, the correct buffer construction for Mono16 and Coord3d_ABC32f frames, and the library versions that carried the bug. For the wider picture on turning raw frame payloads into processable arrays, start with image bytes vs pixel data: decode before processing.
The symptom: the array is short by a clean factor
Divide the element count you received by the count you expected before you touch the camera. That one division names the defect, because this failure always returns the element count divided by the bytes per element:
- 153,600 ÷ 307,200 = 0.50 for a Mono16 frame at 640 × 480 — a factor of 2, the itemsize of uint16.
- 230,400 ÷ 921,600 = 0.25 for a Coord3d_ABC32f frame at the same size — a factor of 4, the itemsize of float32.
An 8-bit format on the same device returns full size, because its divisor is 1. A clean 0.50 or 0.25 means the full payload reached your process and was mis-read there. Any other ratio — 0.75, a third, an irregular number — is a different fault, covered near the end.
Why the count drops: frombuffer counts elements, not bytes
In numpy.frombuffer, the count argument is a number of elements of the dtype you pass, never a number of bytes — the frombuffer documentation is explicit. A byte count pairs with dtype='uint8'; an element count pairs with the final dtype of the pixel format. The reported defect — in version 1.2.2 of the Harvester acquisition library for GenICam cameras — crossed that pair. The payload was read with dtype='uint8' while count carried the pixel count, and the format-specific expansion step then re-declared the dtype:
array = numpy.frombuffer(
self._buffer.raw_buffer,
count=int(count), # pixel count: only width × height bytes are taken
dtype='uint8',
offset=self.data_offset,
)
return pf_proxy.expand(array) # the view at the real dtype divides the count again
A dtype re-declaration moves no data; the element count divides by the ratio of itemsize. The two mistakes stack into one clean ratio:
itemsize is 1 byte for Mono8 and Confidence8, 2 for Mono16, 4 for any float32 component. That is why only the wider formats failed.
Worked example: sizing a 640 × 480 Mono16 frame
Inputs, each with its source: Width = 640 px and Height = 480 px from those device features; Mono16 = 16 bits per pixel = 2 bytes per element; components = 1; the payload the device announces for the frame is 614,400 bytes. The rule in words: read the payload at the final dtype with an element count, and the array's nbytes must land on the announced payload size.
The arithmetic:
- pixels = 640 × 480 = 307,200
- expected bytes = 307,200 pixels × 2 B = 614,400 B, equal to the announced payload
- defect path: 307,200 taken as uint8 elements consumes 307,200 B, half the payload, and the view as uint16 gives 307,200 ÷ 2 = 153,600 elements
- ratio test: 153,600 ÷ 307,200 = 0.50, which convicts the count-and-dtype pair
The corrected construction, with the assertions that prove it:
import numpy as np
raw_buffer = frame.raw_buffer # bytes: the frame payload from the transport layer
data_offset = 0 # int, bytes: pixel data starts at 0 here
width, height = 640, 480 # int, px: live values of Width and Height
itemsize = 2 # int, bytes: uint16, from the 16 in Mono16
img = np.frombuffer(raw_buffer, dtype=np.uint16,
count=width * height, offset=data_offset)
assert img.size == 307200 # full element count, not 153600
assert img.nbytes == 614400 # bytes: equals the announced payload
img = img.reshape(height, width) # row-major: height first, then width
Decision: nbytes equal to the payload size means the transfer is complete and the array is whole. An array whose nbytes falls short of the payload by exactly its itemsize factor was built with the wrong count — rebuild it, do not re-grab it.
The 30-second test
- Compute expected elements = Width × Height × components from the device's current feature values, not from the sensor's full resolution.
- Print array.size and array.nbytes; for any array, nbytes = size × itemsize.
- Compare nbytes with the payload size announced for the format.
- Divide the received element count by the expected one: 0.50 or 0.25 convicts the dtype path; 1.00 with a wrong shape is only a reshape mistake.
- Re-run the two assertions from the worked example after any change — one line each, no re-grab needed.
The fix: pair the count with the dtype
Fix it at two layers. The library first: the defect was reported against Harvester 1.2.2, corrected on its _issue_165 development branch and released as 1.2.3 on PyPI; after that update Mono16, Confidence8 and Coord3d_ABC32f all return full-size arrays. Moving to 1.2.3 or later removes the symptom at the source.
Then your own code, whatever library feeds it. Count bytes only with dtype='uint8'; count elements with the format's true dtype; never widen an array by re-declaring its dtype unless you divided the count by itemsize yourself. A multi-component format such as Coord3d_ABC32f arrives as interleaved A, B and C values — check multi-channel image buffer size before processing covers splitting those planes without a second size surprise.
Which formats trip it, and which cameras send them
| Format | Bits per element | Elements at 640 × 480 | Returned by the defect | Ratio |
|---|---|---|---|---|
| Mono8, Confidence8 | 8 | 307,200 | 307,200 | 1.00 |
| Mono16 | 16 | 307,200 | 153,600 | 0.50 |
| Any float32 depth map | 32 | 307,200 | 76,800 | 0.25 |
| Coord3d_ABC32f | 3 × 32 | 921,600 | 230,400 | 0.25 |
The 8-bit row is the control experiment: it cannot fail, which is why a camera looks healthy on Confidence8 previews and still mangles every wider format. Point-cloud work meets these formats in the 3D sensors family — the MV-DT01WMU is a time-of-flight camera — and its Confidence8 map needs its own decode step, covered in 3D camera confidence image values: decode bitflags and mask bad pixels. Mono16 streams come from mono area scan cameras; the MV-CS200-10UM is a 20 MP mono camera with a 1" sensor on USB3. Two cameras that put the whole table on your bench:
- Type
- Depth / ToF camera
- Sensor format
- 1"
- Lens mount
- C-mount
Packed 10-bit and 12-bit formats are the exception to direct sizing: two values share byte groups, so bytes per pixel is not an integer multiple of itemsize. Packed Bayer 12-bit conversion to a 16-bit image shows the unpacking route, and saving 12-bit camera images without losing intensity data keeps the extra range through to disk. If a 10-bit sensor hands you plain 8-bit data, that choice lives in the PixelFormat feature, not the buffer — ADC bit depth vs pixel format: why 10-bit sensors ship 8-bit images separates the two.
Laser profilers and stereo 3D cameras
170 models · from $699.00When the short array is not this bug
A ratio other than a clean 0.50 or 0.25 has other causes, in order of likelihood. Decimation shrinks the image before any buffer exists, by whole factors the camera reports — depth image resolution smaller than expected: check decimation first walks that setting. Binning and a programmed region of interest change the Width and Height features themselves, so read the live values rather than the sensor's maximums. A dropped packet leaves nbytes short of the payload by an irregular amount and surfaces as a resend or a timeout, never as a clean factor. And when two streams share buffers, one frame can arrive carrying the other's dimensions — mixing depth frames from two cameras: find the buffer bug is that failure mode.
Checklist before you blame the camera
- Received ÷ expected = 0.50 or 0.25: the count-and-dtype defect. Fix the construction, not the grab.
- array.nbytes equal to the announced payload: the camera sent everything. Stop swapping cables.
- Read Width, Height and PixelFormat from the device at run time; never assume the full-sensor defaults.
- Count bytes with uint8, elements with the final dtype — one pairing, never crossed.
- Packed 10- and 12-bit formats: use the library's converter, not frombuffer plus a dtype re-declaration.
- After any acquisition-library update, re-run the two assertions from the worked example.
Run the sizing test on your next frame. If a format still refuses to add up, bring the five numbers — Width, Height, PixelFormat, array.size, array.nbytes — to us through the contact page, and browse the camera finder for the sensors behind each format family.
GigE, USB3, 10GigE and CoaXPress
1,287 models · from $149.00Questions
Why did 8-bit formats such as Confidence8 still return the right size?
Because the defect divides the element count by the new itemsize, and an 8-bit format reinterprets uint8 data as uint8, so the divisor is 1. Every one-byte-per-element format — Mono8, Confidence8, Bayer8 — therefore looked healthy on the same camera that mangled Mono16 and Coord3d_ABC32f. Correct 8-bit streams beside broken wider ones is the clearest signature of the bytes-versus-elements defect rather than a transport fault.
What payload size should a 640 × 480 Coord3d_ABC32f frame have?
640 × 480 pixels × 3 components (A, B, C) = 921,600 float32 elements, and 921,600 × 4 bytes = 3,686,400 bytes. Read the frame as float32 with count = 921,600 and the array's nbytes lands on exactly 3,686,400. The defective path returned 230,400 elements — a quarter — because it took 921,600 bytes as uint8 and then re-declared them as 32-bit floats.
Does a numpy view() copy or change the data?
No. view() returns a new array object looking at the same memory with a different dtype, so no bytes move and none are lost — only the element count changes, because nbytes = size × itemsize must stay true. That invariant is your debugging handle: nbytes does not change across the view, so compare nbytes against the announced payload before you suspect the camera of sending less data.
How do I size packed 10-bit and 12-bit formats the same way?
You mostly should not size them that way. Packed formats squeeze two 10- or 12-bit values into non-byte-aligned groups, so bytes per pixel is fractional and set by the packing scheme, not by a single itemsize. Route those payloads through the acquisition library's own converter or a dedicated unpacking routine instead of frombuffer plus view — converting a packed 12-bit Bayer frame to 16-bit is a job of its own.
My array is smaller than the sensor's full resolution — is that this bug?
Only if the shortfall is exactly the itemsize ratio. Otherwise read the Width and Height features the device currently reports: binning, decimation and a programmed region of interest all shrink Width and Height upstream of any buffer math, and the payload shrinks with them by design. A clean half or quarter points at the dtype defect; any other factor points at those acquisition settings.

