You set a monochrome camera to Mono8, and the image arrives as three channels. The hover readout in the viewer lists R, G and B values, your array has three planes, and every saved file is about three times the size you budgeted. Before you change anything on the camera, hold one fact: Mono8 is one channel, one byte per pixel, by definition. A mono camera never triples its own output. The extra channels appear downstream — in the viewer's RGB display surface, in a conversion step, or because the camera is streaming a color format after all. This guide gives you the byte-level definition, three two-minute tests, four causes in order of likelihood, and the fix for each. It applies to any GenICam camera: line scan cameras and area scan cameras over GigE, USB3, Camera Link or 10GigE.
The decision up front
Run the three tests below and commit to the matching fix:
- Byte ratio 1.0, with R equal to G equal to B everywhere: the camera is fine. The three channels are the viewer's RGB display surface. Change nothing; read your gray value from any single channel.
- Byte ratio 3.0, channels still equal: your software spread one byte into three. Fix the conversion, not the camera.
- The camera's PixelFormat node reports anything other than Mono8: the stream really is color, or 10/12-bit mono. Stop acquisition, set the format you want, start again, and read the node back to confirm.
In all three cases the camera is not sending three channels, and nothing needs replacing. The question comes up most often with mono line scan cameras, where viewers and software default settings vary the most:
- Lens mount
- M42
- Interface
- GigE
- Line length
- 2K
- Pixel size
- 7 μm
What Mono8 means, in bytes
Mono8 is a name from the Pixel Format Naming Convention that GenICam cameras follow: the name states the component count and the bit depth. Mono means one component, and 8 means 8 bits, byte-aligned, so each pixel fills exactly one byte. A Mono8 line of 4096 pixels is 4096 bytes before any alignment padding. Mono10 and Mono12 need two bytes per pixel, and 12-bit mono also ships in packed forms that store two 12-bit samples in three bytes.
| Pixel format | Channels | Bytes per pixel |
|---|---|---|
| Mono8 | 1 | 1 |
| Mono10 or Mono12, unpacked | 1 | 2 |
| Packed 12-bit mono | 1 | 1.5 |
| Bayer, 8-bit | 1 (mosaic) | 1 |
| RGB8 | 3 | 3 |
| 32-bit BGRA-type | 4 | 4 |
Two lessons hide in that table. A Bayer frame is single-channel too — one byte per pixel in its 8-bit form — and only becomes color after demosaicing, so one channel does not always mean finished monochrome data. And the channel count is not the byte count: packed 12-bit mono has one channel at 1.5 bytes per pixel. The byte count is the number that never lies.
Three tests that settle it
Test 1: read the format from the camera. Query the PixelFormat node after acquisition starts, and read what the camera reports, not the value your configuration code wrote. Nodes have readability rules of their own, covered in camera feature node readability: check before reading. If a write fails, list the camera's symbolics before choosing a value; PixelFormat invalid value: list the camera's symbolics first shows the procedure.
Test 2: count bytes. Divide the byte count your grab call reports by the pixel count you expect:
# Illustrative variables; replace them with your grab call's values
line_length_px = 4096 # int: pixels per line, from the camera's line length
lines_per_frame = 8 # int: lines buffered per acquisition
buffer_bytes = 32768 # int: byte count reported by the grab call
ratio = buffer_bytes / (line_length_px * lines_per_frame)
print(ratio) # 1.0 -> Mono8 as sent; 3.0 -> tripled downstream
Here 4096 × 8 = 32768, and 32768 / 32768 = 1.0: one byte per pixel, exactly what Mono8 promises. A clean 3.0 is a conversion, never a camera property. A ratio of 2.0 or 1.5 sends you to cause 4.
Test 3: compare channels. Sample the pixel you hover over. R equal to G equal to B means gray data in a three-channel container, and any single plane of it is the mono image you wanted.
Cause 1: the viewer paints gray through an RGB surface
Most machine-vision viewers render into a 24-bit RGB surface, because that is what the monitor takes. Gray level 137 is painted as R 137, G 137, B 137, and the hover readout honestly reports all three components. That is the whole mystery behind the complaint that a viewer shows RGB on a mono8 image: three equal numbers are what gray looks like in RGB. Nothing is wrong, and the camera is doing exactly what you asked.
Some viewers go further and apply a false-color palette to mono data, mapping intensity to color so faint defects stand out. Then the three values differ and the frame looks colored even though the buffer has one channel. Look for a palette, LUT or color-map setting and switch it back to gray.
The fix is nothing at all on the camera. If your processing consumes what the viewer saves, take one plane and treat it as the mono image; averaging the three planes adds a pass over the data and rounding noise for zero new information.
Cause 2: your conversion spreads one byte into three
Somewhere between the grab and your processing, a library builds a three-channel image because the next stage expects one: an overlay wants BGR, a file writer's default is a color container, a message type declares three planes. The classic case in robot code is cv_bridge handing you a color encoding unless you ask for the mono one, covered in cv_bridge 8UC1 vs mono8: fix grayscale image encoding errors. The same class of bug appears as doubled or sheared frames when the Mat type does not match the pixel format, in colour frame comes out doubled: fix Mat type pixel format mismatch, and as torn images when a buffer is queued before the copy finishes, in torn frames in a camera callback: copy the buffer before you queue it.
Count the cost before dismissing it as cosmetic. The LS-CMR-0166 delivers 4000 × 3000 frames at up to 30 fps over USB3. One 4000 × 3000 frame in Mono8 is 4000 × 3000 × 1 = 12,000,000 bytes, about 11.4 MiB. Forced into three channels, the same frame is 36,000,000 bytes, about 34.3 MiB, plus a full extra copy pass to build it.
The fix: request the single-channel type at every hop — grab, convert, process, save — and write mono to a single-channel PNG instead of an RGB container.
The area scan camera behind those numbers:
- Sensor format
- 1/1.7"
- Interface
- USB3
- Resolution
- 12 MP
- Shutter
- Rolling
Cause 3: the camera is not streaming Mono8 at all
You may simply not be receiving Mono8. Cameras power up in a default pixel format, and on color models that default is a Bayer or YUV format, not a mono one. The mirror-image failure, a color camera arriving gray because nobody requested a Bayer format, is covered in camera image encoding falls back to mono8: request Bayer instead and GigE camera shows no color image: fix the pixel format default.
Test 1 settles it: the PixelFormat value as the camera reports it. A mono model lists only Mono entries among its symbolics; a color model adds Bayer, RGB and YUV entries, and it will never report Mono8 for data its color sensor captured. Decide which kind you own and set the format deliberately.
The fix has an order to it. Most GenICam cameras refuse a PixelFormat write while grabbing, so stop acquisition, write Mono8 — or the color format you actually want — start acquisition, then read the node back to confirm it took.
One color model and one mono model, for contrast:
- Lens mount
- M42
- Interface
- GigE
- Sensor format
- 1"
- Lens mount
- C-mount
Cause 4: 10- and 12-bit mono read as bytes
If test 2 returned 2.0, the camera is sending Mono10 or Mono12 unpacked, at two bytes per pixel. If it returned 1.5, you have a packed 12-bit format, two samples in three bytes. Either way, a reader built for one byte per pixel walks the buffer with the wrong stride, and the image comes out as diagonal garbage, shifted bands, or a width that no longer matches the data — which downstream code often misreports as a channel problem.
The fix is to match the container to the format. Request Mono8 if 8 bits of depth are enough for the inspection. If you need the 12 bits, unpack into a 16-bit container and check how your library fills it: values that top out near 65520 are 12-bit data shifted into the 16-bit range, not amplified. The arithmetic is worked through in 12-bit data in a 16-bit V4L2 frame: why values top out near 65520.
Worked example: one line, counted
Put numbers on the line-scan case. A 410 mm web passes under a 4096-pixel mono line scan camera, so sampling is 410 / 4096 = about 0.1 mm per pixel. The web runs at 2.5 m per second, which is 2500 mm per second, so the camera needs 2500 / 0.1 = 25,000 lines per second to keep its pixels square. Encoder setup has to agree with that scale; encoder ticks per pixel for line scan: set the scale right covers that side.
Data rate in Mono8: 4096 bytes per line × 25,000 lines per second = 102,400,000 bytes per second, about 102 MB/s. A 1 GigE link carries roughly 100 to 120 MB/s of image data with jumbo frames enabled, and the packet-size side of that budget is its own topic in 10GigE camera at only 1 Gbps throughput: fix packet size and NIC setup. Mono8 fits with modest headroom.
Tripled to three channels, the same line is 4096 × 3 = 12,288 bytes, and the rate becomes 12,288 × 25,000 = 307,200,000 bytes per second, about 307 MB/s. The link saturates, buffers back up and lines drop, so the symptom you chase becomes tearing or missing lines instead of too many channels. That is the real price of the conversion bug: it starves the pipeline, not just the disk.
The LS-LCM-0080 is a 4K mono GigE line scan camera with an effective pixel count of 4096 × 8, the line length this example assumes. Whatever grows beyond that on your side of the cable was added by software.
A real camera with that line length:
- Lens mount
- M42
- Interface
- GigE
- Sensor format
- APS-C
- Shutter
- Global
When three channels are the right answer
Sometimes the RGB version is legitimate: you asked a color camera for RGB8, your overlay library accepts only BGR, or the deliverable is a report image for humans. The test is whether the choice was yours and whether the channels differ. Equal channels at three times the bytes is a bug; different channels at three times the bytes is color.
Prevention checklist and next step
- Read PixelFormat back from the camera after the first frames arrive, and log it next to the device serial.
- Assert buffer bytes = pixels × bytes per pixel at start-up, and fail loudly on 3.0.
- Name the single-channel encoding at every boundary: grab, message, Mat, file.
- Save mono as single-channel PNG, and reserve three-channel files for real color.
- Change pixel formats with acquisition stopped, and on a line, with the line stopped.
If the fix points at the hardware rather than the code, the camera finder filters by line length, interface and spectrum, and the configurator walks the same choices step by step. For a stream that resists diagnosis, send the PixelFormat readback and one buffer's byte count through the contact page and we will work it with you.
Questions
Is the Mono8 pixel format one channel or three?
One. The naming convention behind GenICam pixel formats defines Mono8 as a single-channel, byte-aligned format: one 8-bit byte per pixel and nothing else, so a 4096-pixel line is exactly 4096 bytes before any alignment padding. Any second or third channel you see was added after the camera, by a viewer, a conversion layer, or a file save.
Why does the viewer show R, G and B values when I hover over a mono8 image?
Because the display surface behind the viewer is RGB. Gray value 137 is written into all three components, so the readout lists the same number for R, G and B. Three equal numbers are the signature of monochrome data in an RGB container. If the values differ widely on a mono source, the viewer has applied a false-color palette; switch it back to a gray map.
How can I prove what my buffer contains without opening a viewer?
Divide the byte count reported by your grab call by the pixel count you expect. A ratio of 1.0 means Mono8 as sent, 2.0 means Mono10 or Mono12 unpacked, and 1.5 means a packed 12-bit format storing two samples in three bytes. A clean 3.0 means software tripled a Mono8 buffer somewhere between the camera and your array. Log the ratio once at commissioning and alarm on any change.
Can a monochrome camera output a color pixel format?
No. A mono sensor's format list contains only Mono entries. If the PixelFormat node reports a Bayer or RGB format, you are talking to a color model, or to a different camera than the one you configured, so check the device serial and not just the address. Color line scan and area scan cameras exist; they simply are not monochrome cameras.
What does tripling mono8 to three channels actually cost?
Three times the memory, three times the copy time, and on line scan three times the link load. A 4096-pixel Mono8 line is 4096 bytes; as three channels it is 12,288 bytes. At 25,000 lines per second that is the difference between about 102 MB/s and 307 MB/s, which decides whether a 1 GigE link keeps up with the web.




