A stitching run that needs a 32 GB mosaic fails on the allocation call even on a 64-bit PC with 64 GB of RAM, and the error text blames memory. The refusal is not the operating system: most machine-vision image containers count buffer elements with 32-bit integers and stop at 2,147,483,648 of them per image. This guide is for anyone stitching camera grids — flat-panel and PCB surveys, wide webs, line-scan sweeps — into one image. You will compute the true per-image capacity for each pixel format, choose between strip buffers and a language-level array, write the mosaic to PNG strip by strip, and size the camera and network side so the run finishes.
Why does image allocation fail on a 64-bit PC with plenty of RAM?
Because the refusal happens inside the vision library, not in the OS: image containers count buffer elements with 32-bit integers, so a single image stops at 2,147,483,648 elements — 2 GiB of buffer — long before the installed RAM matters.
A 64-bit process has terabytes of address space, so the OS is never the one saying no at this size. The call computes width × height × bytes per pixel, sees a result above 2^31, and returns failure without asking the OS to reserve a single byte. One documented form of that failure is error 0xBFF60768, whose text reads "the operation would have exceeded the capacity of an internal container, which is limited to 2147483648 unique elements." If your error mentions an internal container and that number, you are at the container ceiling, not out of memory.
Two checks before redesigning anything. First, confirm the process really runs 64-bit: some vision SDKs ship .NET assemblies built 32-bit only, and loading one drags the whole application into a 2 GB address space, where every large allocation fails for a different reason. Second, make sure small tiles allocate normally; if even a 100 MB buffer fails, the problem is host or driver buffer starvation, which Diagnose frame grabber buffer allocation errors walks through.
What exactly is the 2 GB single-image limit?
It is a cap on the whole image buffer — 2,147,483,648 elements, i.e. 2 GiB of bytes — not a cap per dimension; width and height may individually be very large, but their product times the bytes per pixel cannot pass the ceiling.
The details vary by library, and they matter for the design. Some SDKs take width and height as 32-bit integers, so a one-row image can be declared billions of pixels wide even though a full 2D buffer is capped. Some cap total buffer size at 2 GB in every pixel format. Some cap one dimension far lower — one library documents 65,535 pixels per side because its region masks use 16-bit coordinates. Kernel libraries that accept buffer size as a plain int keep a 2 GB ceiling on 32- and 64-bit systems alike, which is why "we are 64-bit now" changes nothing. Large-buffer budgets like this recur across acquisition and processing; the machine-vision systems topic page groups the related guides.
How much mosaic fits in one image at each pixel format?
Divide the 2,147,483,648-byte ceiling by the bytes per pixel: RGB32 fits 536,870,912 pixels, RGB24 fits 715,827,882, 16-bit grey fits 1,073,741,824, and 8-bit mono fits 2,147,483,648 — about 2.1 gigapixels.
| Pixel format | Bytes per pixel | Max pixels per image | Shape at the cap |
|---|---|---|---|
| Mono 8-bit | 1 | 2,147,483,648 | 32,768 × 65,536 |
| Grey 16-bit | 2 | 1,073,741,824 | 16,384 × 65,536 |
| RGB 24-bit | 3 | 715,827,882 | 10,922 × 65,536 |
| RGB 32-bit | 4 | 536,870,912 | 8,192 × 65,536 |
Row padding adds a few bytes per row in some SDKs and changes nothing at these widths. In camera terms, one RGB32 image holds about 44 frames of a 12 MP camera (536,870,912 ÷ 12,000,000 = 44.7), so a grid bigger than roughly 45 such frames cannot live in one container. Treat the table as the design boundary: a mosaic past it must be split, whatever happens downstream.
Can I raise the 2 GB limit with a runtime setting?
No: nothing in the OS or the compiler lifts it. The failing check is the library's own element counter, evaluated before any allocator is asked for memory, so a 64-bit build, a large page file and extra RAM all leave the ceiling at 2,147,483,648 elements.
Settings do remove the other ceiling. Compile as x64 or any-CPU so the process gets full address space, and use the SDK build your vendor ships for 64-bit — some SDK generations provide 32-bit-only assemblies for one language binding and 64-bit for another. Newer .NET runtimes also offer a configuration switch that lets plain language arrays exceed 2 GB per object; that helps your own arrays, not the SDK's image class. The per-image container cap itself has outlived more than a decade of 64-bit SDK releases, so design as if it is permanent. The legitimate fix is not a setting but the data model: stop needing one giant image.
How do I stitch large images without one giant buffer?
Split the mosaic into strips that each stay under 2 GiB, stitch and process them one at a time, and write each finished strip to disk as it completes; peak memory then grows with strip size and overlap, not with the mosaic.
You need a tile grid plan (tile size and overlap), a position source (stage or encoder counts, or feature alignment between neighbours), two or three strip buffers, disk space, and a manifest file. Then:
- Fix the grid and the overlap. One tile is one camera frame of W × H pixels; use 10-20% overlap; each step advances W × (1 − overlap) horizontally and H × (1 − overlap) vertically.
- Compute strip bytes and check the boundary: strip pixels × bytes per pixel ≤ 2,147,483,648. Use the format table above.
- Allocate two or three strip buffers — current, prefetch, and previous for seam blending — not one per tile. Peak RAM is about 3 × strip bytes.
- Acquire tile by tile, with cameras pinned to ports so a reboot never reshuffles the grid: see Assign cameras to ports reliably in a multi-camera system.
- Align each new strip against the previous strip's overlap band, and keep every origin as a 64-bit integer.
- Write the finished strip to PNG, append its origin to the manifest, then release its buffer. Across hundreds of tiles, a small leak per tile ends the run; Preventing memory leaks when calling a vision library from C# shows the ownership pattern.
- Advance the grid and repeat until the last strip is on disk.
- Verify: manifest rows × strip bytes must sum to the planned mosaic bytes, and each re-read file must match the checksum logged at write time.
If you would rather hold the mosaic in memory as one logical object, use your language's own 2D byte array instead of the SDK image class. Per-dimension caps sit near 2^31 elements, but the total is not capped the same way: the example mosaic below is 96,000 rows × 360,000 bytes per row, both dimensions far under the cap, 34,560,000,000 bytes in total, and you write strips out of it block by block.
How does the arithmetic work out for a 32 GB mosaic?
Take 30 × 32 tiles of 4000 × 3000 pixels in RGB24: the mosaic is 120,000 × 96,000 pixels — 11,520,000,000 pixels, 34,560,000,000 bytes, or 32.2 GiB — which is 16 times past the RGB24 single-image cap of 715,827,882 pixels.
- Mosaic: 30 × 4,000 = 120,000 px wide; 32 × 3,000 = 96,000 px high.
- Bytes: 120,000 × 96,000 = 11,520,000,000 px; × 3 B = 34,560,000,000 B; ÷ 1,073,741,824 = 32.2 GiB.
- One-image test: 11,520,000,000 ÷ 715,827,882 = 16.1, so SetSize fails at 16 times the ceiling.
- Strip plan: 32 strips of 120,000 × 3,000 = 360,000,000 px; × 3 B = 1,080,000,000 B = 1.01 GiB each, under 2 GiB with room for stride and overlap.
- Memory: three strips is about 3.0 GiB plus SDK and OS overhead, so a 16 GB machine runs this plan; the 64 GB machine that refused the monolithic image has room to spare.
- Tile count: 960 frames without overlap; with 10% overlap you need 34 × 36 = 1,224 frames to cover the same area, because (1 ÷ 0.9)² ≈ 1.23.
- Time: the 12 MP USB3 camera delivers 4000 × 3000 frames at 30 fps. At that rate, 1,224 frames is 40.8 s of exposure for one camera, or about 10.2 s spread across four cameras, before stage motion.
- Sensor format
- 1/1.7"
- Interface
- USB3
- Resolution
- 12 MP
- Shutter
- Rolling
Disk is the last budget line: raw strips total 34.56 GB, and PNG compression on textured surfaces typically lands between 1:2 and 1:4, so budget roughly 9-17 GB and confirm the ratio with one strip of your own content. The verdict is the point of the exercise: the same PC that could not hold the mosaic runs this plan in about 3 GiB of RAM and writes it strip by strip. Any camera in the area scan cameras range can feed the grid; what matters is pixels per frame and the interface's sustained rate.
How do I write the mosaic to PNG tile by tile?
Write each strip to its own PNG the moment it is final and log its pixel origin in a manifest; the PNG format allows sides up to 2,147,483,647 pixels, so the file format never forces a giant in-memory image.
The PNG specification stores width and height as 4-byte unsigned integers, which is why a 120,000 × 3,000 strip — or even the full 120,000 × 96,000 mosaic, on paper — is expressible in the format. In practice, keep the per-strip files: any loader that opens one huge file rebuilds a full-size buffer and recreates the same ceiling at read time. Encode on the critical path with a moderate compression level, write RGB24 directly, and drop the unused alpha if your buffer is RGB32. If you write 16-bit grey strips, expect some viewers to preview them nearly black; that is viewer scaling, as Why 10-bit PNG images look black in image viewers explains.
Verify the run before trusting it:
- Re-open one strip per grid row and check its dimensions and origin against the manifest.
- Checksum each file as it is written; a mismatch almost always means the buffer was freed mid-encode.
- Cross-correlate the overlap band of adjacent strips; a dropped peak means the overlap was mis-set on that axis.
Common mistakes, in the order they usually cost an afternoon:
- Origins multiplied in 32-bit integers, which overflow silently past 2,147,483,647.
- Ignoring row stride: buffer bytes equal stride × height, not width × bytes per pixel × height, whenever the SDK pads rows.
- Counting the overlap on acquisition and again on stitching, which doubles the band.
- Writing a strip before its seam blend with the next strip is done, then rewriting it.
What should I check before ordering the acquisition hardware?
Size the acquisition link from the tile rate, not the mosaic size: a 4000 × 3000 RGB24 tile at 30 fps is 4000 × 3000 × 3 × 30 = 360 MB/s per camera, which one GigE link cannot carry but USB3 can.
Run the same arithmetic per camera: pixels per frame × bytes per pixel × frames per second. A 12 MP camera at 30 fps in RGB24 asks for 360 MB/s, while GigE payload tops out near 110-118 MB/s, so that stream needs USB3 (about 400 MB/s effective) or a 2.5G or 10G link. Total it across the grid: four such cameras push 1.44 GB/s toward disk, which means NVMe, because a SATA SSD tops out near 0.5 GB/s. If processing cannot keep pace with acquisition, that gap is its own topic: Acquisition faster than processing: fix the frame rate gap.
For a multi-camera grid, spread the links across independent ports. The MV-GE2002P is a dual-port PoE NIC in a full-height card, and the MV-GE1104 is a 4-port GigE frame grabber; either way, each port carries one camera's traffic on its own link, and the port-to-camera map belongs in the manifest, not in enumeration order.
- Interface
- GigE
- Ports
- 2 ports
- Interface
- GigE
- Ports
- 4 ports
RAM needs no drama: three strips plus SDK and OS overhead sit near 6-8 GB, so 16 GB runs the example and 64 GB leaves room for a second grid. Budget disks for sustained writes rather than bursts; the example writes 34.56 GB across the run at full rate.
CoaXPress, Camera Link and GigE cards
58 models · from $189.00If you are planning a tiled rig, check the two budgets in this order: pixels per frame at the tile rate first, then bytes per strip against the 2 GiB line. The camera finder filters cameras by resolution and interface, and the camera configurator walks the rest of the chain in the same sequence. For an unusual grid — odd tile shapes, encoder-driven stages, line-scan sweeps — send us the mosaic size and tile rate through the contact page and we will size the acquisition path with you.
Questions
Will more RAM lift the 2 GB image limit?
No. The ceiling is an element counter inside the image container, checked before the OS allocator is ever asked for memory, so it behaves identically on a 32 GB and a 256 GB machine. Extra RAM still helps the tiled workflow: it lets you hold more strips and their overlap buffers at once, which shortens each run.
Can a GPU stitch the mosaic instead?
A GPU moves the pixels but not the data model: card memory is typically 8-24 GB and PCIe 3.0 x16 transfers about 16 GB/s, so a 34.5 GB mosaic still crosses the bus and is processed strip by strip. GPUs pay off in the alignment and blending math, not in one giant buffer.
How do I keep tiles aligned across a long stitching run?
Drive tile origins from stage or encoder counts where you can, keep 10-20% overlap for feature-based refinement, and write each strip's origin into the manifest as it completes. Cross-check the seam on the previous strip before releasing its buffer, so drift is caught within one tile cycle instead of at the end of the run.
Is one giant PNG file a better deliverable than strips?
Strips travel better. A single mosaic-scale PNG is legal in the format, but any tool that opens it must build a full-size buffer and recreates the same ceiling at read time. Ship per-strip PNGs plus the manifest; a downstream reader then opens one 1 GiB strip at a time and never needs the whole mosaic resident.
Do numpy arrays or plain C++ buffers hit the same cap?
Not by default. A 64-bit numpy array or a standard library container is bounded by address space and installed memory, so the 34.5 GB mosaic allocates when the machine has room for it. The 2 GiB ceiling appears only in image classes that count buffer elements with 32-bit integers, so keep your big arrays out of those calls.


