Cameras & sensorsHow-to

Decode embedded image metadata chunks to real camera values

Decode the embedded image metadata your camera stamps into a frame's first pixels: mask the flag bits, take the low 16 counts, and convert to milliseconds.

LumaSpec Vision9 min read Share on LinkedIn

On this page 9 sections
  1. 01What are the first pixels of my image storing?
  2. 02Why does 3254784138 not equal my 99.859 ms shutter value?
  3. 03How do I convert shutter counts into milliseconds?
  4. 04How do I decode the four bytes in MATLAB?
  5. 05How do I find the counts per millisecond without the SDK?
  6. 06Do GenICam cameras need the same conversion?
  7. 07How do I verify the decoded values end to end?
  8. 08When is first-pixel metadata the wrong tool?
  9. 09Questions

You captured a run of frames with the per-frame shutter, gain and timestamp stamped into the first pixels of every image, and now your post-processing code must turn those bytes back into real milliseconds. The trap is simple: the stamped value is a raw register word. Flag bits fill the top half, a count fills the bottom half, and units appear nowhere. This guide rebuilds the word from four bytes, converts the count to an exposure time using the register's time base, checks the result two independent ways, and shows when GenICam chunk data removes the conversion entirely. The example code is MATLAB, but the byte math is identical in any language.

What are the first pixels of my image storing?

One 32-bit register word per field, written across four consecutive 8-bit pixels: a camera with first-pixel embedding reserves up to the first 40 pixels of the frame — about ten fields — for values such as shutter, gain, timestamp, strobe and GPIO.

Each field is enabled separately before capture, and the field order and pixel offsets are fixed per camera model and printed in its embedded image information table. On this family the four bytes run from the most significant to the least significant, so pixel 1 of the shutter field carries the flag byte C2. The vendor's viewer shows the same words in hexadecimal; make your assembled word match that display once and the byte-order question is closed for good.

Treat the reserved pixels as a header, never as scene data. Any average, threshold or filter over them returns nonsense, and a bit-depth conversion or color reconstruction on ingest rescales pixel values and destroys the stamped bytes before your code ever sees them.

Why does 3254784138 not equal my 99.859 ms shutter value?

Because 3254784138 is the entire 32-bit word 0xC200108A — control and status flags fill the top 16 bits — while the shutter measurement lives in the bottom 16 bits, 0x108A, which is 4234 counts. The example frame's shutter field reads:

Metadata pixel Decimal Hex Role
1 194 C2 control flags
2 0 00 control flags
3 16 10 count, high byte
4 138 8A count, low byte

Assemble with pixel 1 as the most significant byte: 194 × 16,777,216 + 0 × 65,536 + 16 × 256 + 138 = 3,254,784,138. The count is the last two bytes: 16 × 256 + 138 = 4,234. The 0xC200 half mirrors the property register's on/off, auto and absolute-value bits — status, not measurement. Never feed the full word into a conversion, and never assume the count carries a unit: it is a relative value in the register's own counts.

How do I convert shutter counts into milliseconds?

Divide the count by the register's time base: this shutter register runs at 42.376 counts per millisecond, so 4234 counts ÷ 42.376 counts/ms = 99.92 ms, matching the viewer's 99.859 ms within 0.06 %.

word = b1·2^24 + b2·2^16 + b3·2^8 + b4 counts = word mod 65536 ms = counts ÷ counts_per_ms

The full chain for the example frame:

  • word: 0xC200108A = 3,254,784,138
  • counts: 0x108A = 4,234
  • exposure: 4,234 ÷ 42.376 = 99.915 ≈ 99.92 ms

The base comes from the camera's register table, which prints it as 42.376 counts per millisecond, or 23.6 µs per count; the camera derives it from its sensor clock, and the 0.06 % gap against the viewer is rounding in that published constant. Carry the full base and round once, at the end. A 99.92 ms exposure has two consequences: it fits a stationary scene, and it caps the frame rate near 10 fps — the coupling Why your GigE camera frame rate drops at longer exposure times explains. Each field converts with its own constant from the same table: gain counts with the gain base, the timestamp with its tick rate.

How do I decode the four bytes in MATLAB?

Assemble the four uint8 pixel values into one 32-bit word, keep the low 16 bits, then divide by the counts per millisecond — the asserts prove 4234 counts and 99.92 ms before you trust anything downstream.

  1. Load the frame as a uint8 matrix, untouched by display or bit-depth conversion.
  2. Slice the four bytes at the shutter field's offset from the camera's table; offset 12 stands in as the example.
  3. Assemble the word with pixel 1 as the most significant byte.
  4. Keep the low 16 bits and divide by 42.376.
  5. Assert the known values on a frame you can cross-check in the viewer.
img = rawFrame;                    % uint8 matrix, as delivered
offset = 12;                       % shutter field index, from the camera's table
b = img(1, offset:(offset+3));     % [194 0 16 138] in the example frame

word = uint32(b(1))*2^24 + uint32(b(2))*2^16 + uint32(b(3))*2^8 + uint32(b(4));
fprintf('word %08X\n', word)       % C200108A

counts = double(mod(word, 65536)); % low 16 bits
exposureMs = counts / 42.376;      % counts per millisecond, from the table

assert(counts == 4234)             % low word 108A hex
assert(abs(exposureMs - 99.915) < 0.0005)

In Python the same decode is one line: counts = int.from_bytes(bytes([194, 0, 16, 138]), 'big') % 65536, which returns 4234.

How do I find the counts per millisecond without the SDK?

Command two known exposure times and divide: 2119 counts at 50.000 ms and 4238 counts at 100.000 ms give (4238 − 2119) ÷ (100.000 − 50.000) = 42.38 counts per millisecond, your camera's measured base.

  1. Switch auto exposure off.
  2. Command 50.000 ms, capture ten frames, take the median count: 2119.
  3. Command 100.000 ms, capture ten frames, take the median count: 4238.
  4. Slope: (4238 − 2119) ÷ (100.000 − 50.000) ms = 42.38 counts/ms.
  5. Apply it: the archived frame's 4234 counts ÷ 42.38 = 99.91 ms.

Three-digit agreement with the table's 42.376 is the pass mark. Use the measured slope for the production decode — it absorbs your camera's actual clock, and one count then equals 23.6 µs.

Do GenICam cameras need the same conversion?

No: on a GenICam camera the chunk carries each value in physical units already — exposure in microseconds, gain in decibels — so a frame's 99859 µs is the answer, not the input to a conversion. Enable chunk mode plus the exposure and timestamp chunks, then read each frame's values alongside its pixels: the camera's XML description states every unit, nothing is masked, and no image pixels are consumed.

Three habits from our other guides apply directly. Under auto exposure each frame reports its own value, and Read exposure time from frame metadata under auto exposure shows where it appears. Chunk payloads stay raw numbers until you apply the unit the XML gives, the point Camera exposure and gain units: interpreting raw values makes for register reads. And the timestamp counts from power-up, so latch it at exposure end as Chunk data timestamp: camera time at exposure end and how to latch it describes.

For a new station, pick the camera by interface first: the area scan cameras range spans GigE and USB3, and both speak GenICam, where chunk data is a standard feature a camera can implement. The MV-CS200-10UM is a 20 MP USB3.0 camera. The LS-CMR-0166 streams 12 MP at 30 fps over USB3, and the MV-CH120-11UC pairs a 12 MP global shutter with USB3.

Area scan camerasHikrobot MV-CS200-10UM 20 MP Area Scan Camera, USB3.0, IMX183, 2nd Gen., MonoMV-CS200-10UM
Sensor format
1"
Lens mount
C-mount
Interface
USB3
Resolution
16-25 MP
$759.00View partBuild a system with it
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
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
$2,339.00 · quotedView and quote

The decode runs on the host, every frame, and the vision controllers range includes boxes built for exactly that: the MV-VC3301H-128G66 puts an Intel Core i3, 8 GB of memory and 4 built-in light channels in one chassis, so decode, inspection and strobe timing share a machine. The stream rides one of the camera data cables rated for motion: the LS-CBL-0219, a 3 m USB3.0 high-flex drag-chain cable.

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
Light outputs
4 built-in channels
Memory
8 GB
$2,419.00View partBuild a system with it
Cables3 m USB3.0 camera cable, high-flex (drag chain) (LS-CBL-0219)LS-CBL-0219$62.00View partBuild a system with it

How do I verify the decoded values end to end?

Run four checks: match the camera software's readout to within 0.06 %, keep the count inside the register's printed range, expect a different count on every frame under auto exposure, and multiply part speed by exposure before trusting the frame.

  1. Cross-check one frame against the viewer's absolute-value display: 99.92 ms against 99.859 ms passes, while a factor of 1000 or garbage in the flags points to a unit or masking error.
  2. Range-check the count against the minimum and maximum the table prints; a count of 0 or 65535 means you sliced the wrong pixels or kept the wrong bits.
  3. Watch a live signal: with auto exposure on and the scene lighting moving, counts must change frame to frame; frozen counts mean you are reading image pixels, not the overlay.
  4. Multiply part speed by exposure: at 100 mm/s, 99.92 ms smears 100 × 0.09992 = 9.99 mm — acceptable for a stationary part, unusable for a moving one. Exposure time vs shutter speed for moving-part inspection builds the one-pixel blur budget.

When is first-pixel metadata the wrong tool?

Move to chunk data when you need more than about ten fields, timestamps aligned to exposure end, or values already in units — and keep first-pixel decoding for archives that were stamped with it. The overlay costs the first pixels of every frame and ties your parser to one model's offset table; chunk data costs neither. Decide by case:

  • Existing image archive with the overlay enabled: decode the counts, exactly as above.
  • New station on a GigE Vision or USB3 Vision camera: enable chunk data and read units directly.
  • Multi-camera or strobe-aligned timing: use the timestamp chunk, latched at exposure end.
  • Running line, overlay qualified, five fields or fewer: leave it in place; a working station does not need requalifying for a cleaner interface.

To specify the station, filter the camera finder by interface and resolution, start at the camera step of the configurator, or browse the cameras topic page. For a frame-rate and chunk-support check against your exact task, the contact page reaches the applications team.

Questions

Are the four metadata bytes big-endian or little-endian?

On this camera family the first metadata pixel is the most significant byte, so the field reads C2 00 10 8A for the word C200108A. Settle it against your viewer's hex display: if your assembled word shows 8A1000C2, you assembled the bytes backwards — swap the order and the low 16 bits then hold the count. File writers can byte-swap on save too, so decode from the raw stream, not an exported copy.

Why do I read 99.92 ms when the camera viewer shows 99.859 ms?

The 42.376 counts per millisecond in the register table is a rounded constant; the camera derives the exact base from its sensor clock. The 0.06 % gap is that rounding, not a decode error. Calibrate with two commanded exposures and use the measured slope — 42.38 counts per millisecond here — and your decoded times then track the viewer to within one count, about 24 µs.

Do the stamped pixels corrupt my inspection images?

Only if you process them as image data. The reserved pixels sit at the start of the first row carrying register bytes, so any filter or average over them returns nonsense. Crop the reserved block — 40 pixels on this family — right after decoding, and point the decode at the original frame, not the cropped copy.

Can I read gain and timestamp fields the same way?

Yes. Every field is the same four-byte word, so the byte assembly and masking are identical; only the unit changes. Gain counts convert with the gain table's own base, and the timestamp counts sensor clock ticks, so it needs the tick rate rather than a time base — which is why exposure-end latching matters when you align frames or strobes.

My camera has no first-pixel option. How do I get per-frame values?

Use chunk data: on a GenICam camera you enable chunk mode plus the exposure and timestamp chunks, and every frame arrives with its values in physical units — no offsets to look up, no bits to mask. If a camera offers neither mechanism, the remaining route is the SDK's frame-delivered property readback, which is one reason to specify a GigE Vision or USB3 Vision camera for a new station.