You zoom into a live image in your GenICam viewer and the colors invert. Zoom once more and they flip back; a third step inverts them again, a fourth restores them. Nothing at the camera changes this, and the moment you switch the viewer's renderer from OpenGL to Raster the fault disappears. That combination — clean colors on the CPU path, toggling colors on the GPU path — tells you almost everything: the bytes arriving from the camera are correct, and something in the display path re-interprets them at every zoom step. This guide separates a red-blue channel swap from a brightness inversion, proves either one with a solid-color image and a mono control camera, and gives the fixes in the order to apply them.
What the odd-and-even toggling tells you
A viewer that draws with OpenGL stores the camera frame in a texture and paints that texture through a zoom transform. Zooming should change only the transform: the pixels are already on the GPU, and nothing reads the camera buffer again. So when color flips on one step and flips back on the next, the viewer is doing more than zooming — it re-uploads or re-converts on each redraw, and a swap applied to already-swapped data cancels itself. That is the whole odd-and-even rhythm in one sentence.
The camera never enters the picture. The bytes your code receives and the pixels your code processes are identical at every zoom level, which is why no camera setting changes the symptom. Zoom-dependent rendering faults are not unique to machine vision either; automation vendors carry knowledgebase entries for an OpenGL fix for zoom display artifacts in their simulation software, which tells you this class of bug ships inside finished products.
Read the symptom against this table before touching any code:
| What you see on zoom | Most likely cause | Test that decides it |
|---|---|---|
| Red turns blue at odd steps, back at even steps | BGR buffer uploaded as RGB | Solid-red test image |
| Bright flips dark on alternate steps | Negating shader or per-pass lookup | Mono8 control camera |
| Black, washed-out or noisy output | 8-bit bytes misdeclared as float | Same, at the upload call |
| Skew or tearing as you zoom | Row stride vs byte alignment | Row length in bytes mod 4 |
| Flipped top-to-bottom, constantly | Top-down buffer drawn bottom-up | One-time vertical mirror |
Channel order is the common fault; alignment is the rare one that mimics it badly.
Cause one: red and blue swapped at the upload
OpenGL's upload call, glTexImage2D, takes a format argument that states what the incoming bytes mean: GL_RGB or GL_BGR for 24-bit color. Hand it BGR-ordered data while claiming GL_RGB, and red and blue trade places on screen. Both orders exist in the field: the GenICam pixel-format naming convention defines RGB8 and BGR8 as separate formats, and SDK-side Bayer conversion can be configured to emit either. A viewer that assumes RGB8 while the pipeline delivers BGR8 mislabels every byte.
A single wrong assumption would look wrong at every zoom level, though — constant, not toggling. The odd-and-even pattern needs two alternating paths: typically one upload path that handles the format correctly, and a second, per-zoom path that applies its own swap on top of already-swapped data. Two identical mistakes cancel, which is why even steps look right.
The decisive test is a solid-red image: if red turns blue on odd zoom steps and back on even ones, this is your fault. The same swap away from the display path — files, converted buffers, processing results — has its own write-up in Red and blue channels swapped: why RGB images look wrong.
Cause two: pixel type, and what a mono camera tells you
A wrong type argument — 8-bit bytes declared as GL_FLOAT, or the reverse — behaves differently from a swapped channel order. Byte data misread as floats lands near zero, so frames come out black, washed out or noisy rather than cleanly inverted. That gives you a discriminator for free, because a mono camera removes channel order from the experiment entirely.
Run a Mono8 camera through the same zoom steps. Brightness flipping between dark and light on alternate steps points at a negating shader or a per-pass lookup, not at the upload call. Black or noisy output points at the type argument. And if your color camera quietly delivers Mono8 because a conversion upstream failed, the swap test cannot work at all — the fallback to Mono8 when an encoding request fails is its own trap worth ruling out first.
What to put on the bench
Nothing here needs exotic hardware. Any camera from the area scan cameras range runs the test; the useful pair is one color camera for the swap and one mono camera as the control, at whatever resolution your viewer handles comfortably. A 2.5 MP color GigE camera with a global shutter keeps repaints fast; the 6 MP mono GigE model beside it is the control; the 20 MP color model reproduces the fault on a large canvas; and the 12 MP USB3 camera below contributes its 4000 × 3000 frames to the arithmetic in the next sections.
- Sensor format
- 1/1.7"
- Lens mount
- C-mount
- Sensor format
- 1/1.8"
- Lens mount
- C-mount
- Sensor format
- 1"
- Lens mount
- C-mount
- Sensor format
- 1/1.7"
- Interface
- USB3
Prove it in fifteen minutes
- Build a solid-red test image with no camera at all: 256 × 256 pixels, every pixel RGB8 255, 0, 0, saved as a PNG. Load it in the viewer.
- Zoom in four discrete steps and write down what each shows. Red, blue, red, blue is a channel swap firing on alternate redraws.
- Swap in the mono camera and repeat. Dark-bright alternation indicts a negating shader or lookup; black or noisy output indicts the type argument on the upload.
- Dump the buffer your application receives and read the first three bytes of the first pixel. A solid-red frame must read 255, 0, 0 as RGB8 or 0, 0, 255 as BGR8. Correct bytes with a wrong screen pin the fault to the display path, not to acquisition.
- Switch the renderer to Raster and repeat step 2. Correct color on Raster plus toggling on OpenGL confirms the diagnosis — and hands you a workaround for today's production.
Why zoom triggers it and steady display does not
In a correctly built viewer, the frame uploads once per acquisition. Zoom is then pure GPU math: new texture coordinates or a new projection matrix, repainted each step, with no byte re-read anywhere. The upload call redefines the texture's storage and discards what was there, so every unnecessary re-upload is also a fresh opportunity to mislabel the data.
The waste is measurable, and the buffer arithmetic behind it is the same one you use for acquisition:
Keeping display fed once already moves about 1.08 GB/s for that 30 fps stream; a viewer that re-uploads on every wheel tick pays frame size again per tick. Alignment is the sibling fault: OpenGL's default unpack alignment is 4 bytes, so a row length not divisible by 4 pulls padding into the image as skew or tearing — a constant artifact, never a toggling one.
The fix, in order
- Set the viewer's input format from the camera at runtime. Read the PixelFormat node from the camera's GenICam feature tree and pass the matching constant to the upload: RGB8 with GL_RGB, BGR8 with GL_BGR, Mono8 with the single-channel form. When the format seems stuck no matter what you write, why a camera pixel format refuses to change and how to set it walks through the enumerate-first rule. A viewer that shows no color at all lost the same argument at startup — see the pixel-format default behind a missing color image.
- Convert once, at acquisition. Decode Bayer to RGB a single time per frame, then carry that buffer forward; verify the pixel format on the converted object before it reaches display code.
- Upload once per frame and zoom by transform alone: new texture coordinates or a new projection, never new pixels.
- Until the viewer is patched, run the Raster renderer wherever color fidelity matters. It paints the buffer as-is on the CPU, so there is nothing to mislabel. The same inverted-color symptom is documented well outside cameras; HMI vendors publish their own inverted-colors troubleshooting note for an industrial display panel.
- Report the fault upstream with the two-step reproduction: solid-red image, zoom-step log, buffer dump. A maintainer can close that in one sitting.
The host matters less than the viewer, but if you run the viewer on a vision controller beside the camera the fix is the same: the model below drives cameras from its own LAN ports, has an Intel Core i3 and 8 GB of memory, and carries a VM software license.
- LAN ports
- 5+ LAN ports
- USB 3 ports
- 3-4 USB 3 ports
- Light outputs
- 4 built-in channels
- Memory
- 8 GB
What it is not, and how to keep it away
Not the camera: the buffer dump in step 4 settles that in minutes. Not the link: transport faults on GigE Vision and USB3 Vision surface as resends, dropped frames or torn lines, never as a clean red-blue exchange tied to zoom. Not gamma or color space: those shift tones identically at every zoom, the opposite signature — the linear RGB versus sRGB mismatch covers the consistently-dull case. And not the classic upside-down texture: OpenGL samples textures with their origin at the bottom-left while most image formats store rows top-down, so a missed flip mirrors the image vertically, constantly, at every zoom.
Keep it away
- Solid-color PNGs in the viewer's test set — they catch channel and value faults with no camera attached.
- One conversion, one upload, transform-only zoom — removes every per-render reinterpretation by construction.
- A buffer dump on first bring-up — separates acquisition from display before theories multiply.
- The renderer named in every display bug report — Raster versus OpenGL is the first fact a maintainer needs.
- PixelFormat read from the camera at runtime, never assumed — defaults differ between camera families and firmware generations.
The decision, case by case
Red and blue toggling on zoom with correct color on Raster: a channel swap in the GPU path — set the input format to the camera's PixelFormat and convert once at acquisition. Brightness toggling on Mono8: a negating shader or a wrong upload type — audit the render code, not the camera. Skew or tearing on zoom: row alignment — pad the stride or set unpack alignment to 1. Everything correct on Raster and you need the repaint speed: apply the format fix, return to OpenGL, and keep the solid-red image in the build so a regression cannot sneak back.
If you are outfitting the bench, the camera finder filters by interface, shutter and resolution, the camera configurator pairs the camera with lens and light, and the applications team answers set-up questions through the contact page.
Questions
Why do the colors come back after a second zoom step?
Because the same wrong conversion runs again on already-mangled data: red and blue swap, then swap back. Two identical mistakes cancel, so odd zoom steps look wrong and even steps look right. The camera bytes are identical the whole time; only the display path re-interprets them. The odd-and-even rhythm is the fingerprint of a per-render conversion rather than a bad buffer or a bad cable.
Is the Raster renderer a safe long-term answer?
It is a safe workaround rather than a repair. Raster paints the buffer on the CPU with no texture upload, so there is nothing to mislabel; the cost is repaint speed on large frames. A 36,000,000-byte RGB8 frame repainted at 30 fps moves roughly 1.08 GB/s of memory traffic, which the CPU path feels before the GPU does. Fix the input format, keep OpenGL where speed matters, and keep Raster for color-critical verification.
How do I find out which pixel format the camera is actually sending?
Read the PixelFormat node in the camera's GenICam feature tree at runtime rather than trusting the viewer's default. Mono8 means 8-bit mono, RGB8 and BGR8 are the two 24-bit color orders, and names beginning with Bayer are raw color before conversion. Then check what the viewer was told to expect at its input. When the two names match, the upload call has no guess left to get wrong.
Could the fault be in the camera, the cable or the network?
No. Dump the buffer your application receives: a solid-red test frame must read 255, 0, 0 in the first three bytes for RGB8, or 0, 0, 255 for BGR8. If those bytes are right while the screen is wrong, acquisition is proven innocent. Transport problems on GigE and USB3 links appear as resends, dropped frames or torn lines, not as a clean channel exchange tied to zoom.
Why does my mono image flip brightness while the color camera swaps red and blue?
Mono8 carries one channel, so there is no channel order to get wrong; its inversion points at a different bug in the same path — a negating shader, a per-pass lookup table or 8-bit bytes misdeclared as floats. The color camera's red-blue exchange isolates the channel-order fault. Running both cameras through the same viewer separates the two failure modes in a single bench session.




