System designTroubleshooting

BGR vs Bayer pixel format on GigE: why frame rate drops

BGR vs Bayer on GigE: 3-byte color triples link load and cuts frame rate to a third. The math, packet size and MTU settings, and why host debayering wins.

LumaSpec Vision11 min read Share on LinkedIn

On this page 11 sections
  1. 01The symptom and what it is not
  2. 02Cause one: three bytes per pixel where one used to travel
  3. 03The arithmetic for one camera
  4. 04Cause two: the 9000-byte packet that never passed
  5. 05Set the throughput limit to the link, not above it
  6. 06Fix it in eight steps
  7. 07Prove the fix, then stop
  8. 08When the drop is not the pixel format
  9. 09The decision, case by case
  10. 10Checklist before you close the ticket
  11. 11Questions

An industrial GigE camera that holds 35 fps in BayerRG and drops under 10 fps the moment you switch the pixel format to BGR is working exactly as the physics allows. BayerRG8 puts one byte per pixel on the wire; BGR8 puts three, so every frame triples and the frame rate falls to about a third on a link that was already near capacity. Wrapped around it are two settings that decide whether you keep even that third: the GigE Vision packet size and the link throughput reservation. This guide gives you the bytes-per-pixel arithmetic, the packet size, MTU and switch settings that let jumbo frames through, and the decision for a single camera, a shared link and a 12-bit color pipeline.

The symptom and what it is not

One camera on a switch, streaming BayerRG8 at a steady 35 fps. You change one setting, the pixel format to BGR8, and the frame rate collapses under 10 fps with a single camera connected and no other traffic. Two more clues usually sit next to it: the camera refuses a requested packet size of 9000 and only streams at 1400, and the driver rejects a device_link_throughput_limit of 380000000 with the message that it belongs under 125000000.

None of that is a camera fault. It is two stacked problems: the frame tripled in bytes, and the jumbo frames meant to carry it never passed the network. Fix them in that order.

Cause one: three bytes per pixel where one used to travel

A color sensor does not output three channels. It outputs one intensity byte per pixel under a red-green-blue mosaic, and that mosaic is BayerRG8, one byte per pixel on the wire, exactly like a mono image. Converting to BGR on the camera interpolates all three channels at every pixel, so the same image leaves as three bytes per pixel. The interpolation itself costs almost nothing; the wire pays for it.

bytes per frame = width x height x bytes per pixel payload rate = bytes per frame x frame rate max frame rate = usable payload rate / bytes per frame

For a 1 Gb link, take 110 to 118 MB/s as the usable payload out of the 125 MB/s line rate, after framing overhead, per-frame leader and trailer packets, and resend headroom. The table shows what each format buys at that payload for a 2048 x 1536 sensor:

Pixel format Bytes per pixel Frame at 2048 x 1536 fps at 110 MB/s
BayerRG8 / Mono8 1 3.15 MB 35.0
BayerRG12p (packed) 1.5 4.72 MB 23.3
BayerRG12 / YUV422 2 6.29 MB 17.5
RGB8 / BGR8 3 9.44 MB 11.7

The decision falls straight out of the table: leave the camera on Bayer and debayer on the host. Display viewers and image libraries convert the mosaic on arrival, and anything recorded to disk shrinks by the same factor of three. Most color models list raw Bayer among their pixel formats; two examples:

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 camerasHikrobot MV-CS200-10GC 20 MP Area Scan Camera, GigE, IMX183, Color, BasicMV-CS200-10GC
Sensor format
1"
Lens mount
C-mount
$739.00View part

Pick the BayerRG8 entry in the driver pixel-format list before touching anything else. RGB8 and BGR8 in that list are the entries that triple your bytes.

The arithmetic for one camera

Work the numbers for a 2048 x 1536 color camera at 35 fps, the 3.2 MP class that sits at 35 fps on a 1 Gb link:

  • BayerRG8 frame: 2048 x 1536 x 1 B = 3,145,728 B, about 3.15 MB.
  • Payload at 35 fps: 3,145,728 x 35 = 110,100,480 B/s, about 110 MB/s. In bits, 110,100,480 x 8 = 880,803,840 bit/s, or 0.88 Gbit/s, which is 88% of the line. The camera was already close to the ceiling.
  • BGR8 frame: 2048 x 1536 x 3 B = 9,437,184 B, about 9.44 MB.
  • Bandwidth-limited rate: 110,100,480 / 9,437,184 = 11.7 fps. A measurement under 10 fps fits, because leader and trailer packets, resend margin and host load take the last 10 to 20%.
  • The ratio is the signature: 35 / 11.7 = 3.0. A clean factor-of-three fall is bandwidth, not a defect.

Resolution moves it directly. Halve the pixels and the BGR8 rate doubles. A 2.5 MP color sensor lands in the 12 to 14 fps BGR8 range on one port; a 20 MP sensor at this arithmetic runs BGR8 frames of about 60 MB, and a 1 Gb port delivers roughly 1.8 fps of them. Multiply pixels, multiply bytes. For a concrete pick in these two resolution classes, see the MV-CU020-90GC and MV-CS200-10GC shown above.

Cause two: the 9000-byte packet that never passed

A GigE Vision stream packet rides inside an Ethernet frame. A payload of 9000 B needs 9000 + 14 B header + 4 B checksum = 9018 B on the wire, and every ceiling in the path must clear that number: the NIC MTU, the switch jumbo-frame maximum, and the size the camera and driver negotiate. Set the switch maximum to 9000 and it silently drops every jumbo frame, so the camera stalls and publishes nothing. That is the 9000-blocks-the-driver symptom. A 1400 B packet passes anywhere, because 1400 + 18 = 1418 B fits the standard 1500 B MTU, so the system appears to work at 1400 while paying 6.4 times the per-packet cost: 3,145,728 / 1400 = 2247 packets per frame against 350 at 9000, and the host services every one of them.

Fix the path before blaming the camera: the details are in setting the GigE camera packet size before acquisition starts and capping auto-negotiation when packet size exceeds the MTU. If the camera goes silent the moment it sends test packets, work the GigE Vision test packet that is never received sequence.

Test the path with a large ping before starting acquisition: a 9000 B payload means 9000 - 20 B IP header - 8 B ICMP header = 8972 B of data. If that packet dies on the way, so will the stream.

A jumbo stream also wants a clean cable: shielded Cat5e or better, locking connectors, no kinks. The LS-CBL-0199 is a 3 m high-flex GigE camera cable with screw locks, built for drag-chain use:

Cables3 m GigE camera cable, high-flex (drag chain), screw locks (LS-CBL-0199)LS-CBL-0199$54.00View partBuild a system with it

More cable lengths and connector types sit under GigE cables.

device_link_throughput_limit is a reservation in bytes per second: the camera will not stream above it, so several cameras can share one link without starving each other. It never adds speed. A value of 380000000 B/s reserves about 3.04 Gbit/s, which no 1 Gb port delivers, and that is why the driver insists on a value under 125000000, the 125 MB/s 1 Gb line rate. For one camera per port, 125000000 is right. For cameras sharing a port, divide it: two cameras at 62500000 each hold 62500000 / 3,145,728 = 19.8 fps in BayerRG8.

When several cameras feed one host, plan ports the way the multi-camera GigE bandwidth budget and GigE camera network planning for switch ports describe, and read troubleshooting multi-camera GigE frame drops for the switch-side failure modes. One camera per NIC port is the clean layout. The MV-GE2002P is a dual-port PoE NIC, so each camera gets a dedicated link and its power on the same card:

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

When the layout grows past the onboard ports, browse GigE frame grabbers or the camera finder.

Fix it in eight steps

  1. Set the camera pixel format to BayerRG8, and keep BGR conversion on the host.
  2. Set device_link_throughput_limit to 125000000 for one camera per 1 Gb port, or 62500000 each when two share.
  3. Set gev_scps_packet_size to 9000.
  4. Set the host NIC MTU to 9000 on every interface the stream crosses.
  5. Set the switch jumbo-frame maximum above 9018 B, which is 9216 on most switches.
  6. Ping the path with an 8972 B payload; if it fails, fix the switch before touching the camera.
  7. Start acquisition and read the measured frame rate against the prediction: payload rate divided by frame bytes.
  8. Confirm the host pipeline converts Bayer to the color format your consumer expects, once, after the wire.

Prove the fix, then stop

Expected outcome: BayerRG8 at 3.15 MB per frame returns to 35 fps, because 110 MB/s / 3.15 MB = 35. Switch to BGR8 once as a deliberate diagnostic: the rate should land near 11.7 fps, matching the arithmetic. If it lands far below that, the link rather than the pixel format is losing frames; treat it as GigE Vision packet loss with blank or torn frames and work the switch path.

When the drop is not the pixel format

Exposure caps frame rate before bandwidth does, once it exceeds the frame period: 1 / 35 fps = 28.6 ms. A 50 ms exposure delivers 20 fps in any format. The mechanism is covered in why the GigE camera frame rate drops at longer exposure times, and the same ceiling applies to color streams, as RGB camera frame rate limits from pixel format and exposure lays out.

Two impostors also look like a bandwidth drop. A driver that silently serves Mono8 instead of the Bayer you requested is fixed by requesting Bayer instead of the Mono8 fallback. A viewer that cannot display a packed format and shows a blank preview is the unsupported Bayer pixel format case, not a link problem.

The decision, case by case

  • One camera, one 1 Gb port, color needed for display or processing: stream BayerRG8 and debayer on the host. You get the full 35 fps back, and recordings shrink to a third.
  • The consumer must receive BGR from the camera: budget 9.44 MB per 3.2 MP frame and about 11.7 fps per 1 Gb port. Holding 35 fps in BGR8 needs 9,437,184 x 35 x 8 = 2.64 Gbit/s of payload, so move to a 5GigE or 10GigE link and a matching card; start at the camera step of the configurator to match camera, link and card.
  • 12-bit color: stream BayerRG12p at 1.5 B per pixel, 4.72 MB and about 23 fps at this resolution, and debayer to 16-bit color on the host. BayerRG12 at 2 B, 6.29 MB and 17.5 fps fits pipelines that cannot unpack packed data. BGR8 throws away the low four bits inside the camera; nothing downstream recovers them.
  • Several cameras, one host: one port per camera at 125000000, divide the reservation where they must share, and keep every hop jumbo-clean.

Checklist before you close the ticket

  • Pixel format on the camera: BayerRG8, or BayerRG12p for 12-bit color.
  • device_link_throughput_limit: 125000000 per camera per 1 Gb port.
  • gev_scps_packet_size: 9000.
  • NIC MTU: 9000 on every interface in the path.
  • Switch jumbo-frame maximum: above 9018 B, set to 9216.
  • The 8972 B ping passes camera to host.
  • Measured fps lands within about 10% of payload rate divided by frame bytes.
  • Color conversion happens once, on the host.

If the numbers still will not close, bring the arithmetic and the switch model to the contact page and we will work the path with you.

Questions

Does debayering on the host overload the processing PC?

No, you trade link bandwidth for CPU, and the link was the bottleneck. A bilinear debayer of a 3.2 MP frame is typically single-digit milliseconds on one modern core, against a 28.6 ms frame period at 35 fps. The color conversion was going to happen somewhere; doing it after the wire costs neither frames nor bytes, and anything you record shrinks to a third.

Why does the switch need 9216 when the camera packet is 9000?

A 9000-byte GigE Vision packet rides in an Ethernet frame of 9000 + 14 header + 4 checksum = 9018 bytes. A switch whose jumbo-frame maximum is 9000 drops every one of them, so the camera stalls; 1400-byte packets pass because 1418 bytes fits the standard 1500-byte MTU. The maximum just has to clear 9018, and 9216 is the common ceiling. Raise it on every switch between camera and host.

Should I raise device_link_throughput_limit above 125000000?

No. The value is a reservation cap in bytes per second, not speed you can add: 380000000 B/s would be about 3 Gbit/s, which no 1 Gb port delivers, so drivers refuse it. Keep 125000000 for one camera per port and divide it across cameras that share a link, for example 62500000 each for two. To go faster, cut bytes per pixel or upgrade the link.

Does RGB8 use less bandwidth than BGR8?

No, both are three bytes per pixel, 9.44 MB for a 2048 x 1536 frame, and both land near 11.7 fps on a 1 Gb link. The two formats differ only in byte order, so pick whichever the consuming software expects and stop there. If you need color over the wire at fewer bytes, YUV422 at two bytes per pixel or packed BayerRG12p at 1.5 are the real savings.

How do I prove the link, not the camera, is the limit?

Two quick tests. Halve the region of interest in BGR: if the frame rate roughly doubles, you are bandwidth-bound, because the frame bytes halved. Then shorten the exposure below the 28.6 ms frame period and watch: a bandwidth-limited camera ignores exposure changes, an exposure-limited one tracks them. A bandwidth-bound camera also frees up the moment you switch back to BayerRG8.

Do I lose image quality by keeping 12-bit color as BGR8?

Yes. Converting BayerRG12 to BGR8 on the camera discards four bits of depth per pixel before you ever see the data, and nothing downstream recovers them. Stream BayerRG12p at 1.5 bytes per pixel instead, 4.72 MB per frame in this example and about 23 fps on a 1 Gb link, and debayer to 16-bit color on the host if your pipeline accepts it.