LabVIEW vision code that calls IMAQ Create inside a subVI running at frame rate pays a memory allocation on every scan — and risks deleting an image the rest of the program still holds. This page flips the pattern: create every image reference once at initialization, reuse it in every loop iteration, and dispose once at shutdown. You get the mechanism behind the references, the dispose trap that breaks multi-VI programs, the buffer arithmetic for sizing the pool, and the decision for single loops, deep hierarchies and parallel consumers.
What an image reference actually is
An IMAQ Create call does not hand you pixels. It registers a named image in memory and returns a reference to it, the way a queue reference points at a queue without containing one. The name is the identity of the buffer. Call IMAQ Create twice with the same name and you hold two references to one buffer, not two buffers.
Most vision VIs take a source and a destination reference, and many of them process the source while writing the result into the destination. The destination only has to exist and be large enough, so you rarely need an explicit copy before the call. The reference stays valid for the life of the program unless someone disposes it, which is why the create-once, wire-everywhere style works. The same idea outside LabVIEW, at the SDK level, is covered in Reuse image buffers instead of per-frame allocation.
The cost of creating and disposing inside the loop
Every IMAQ Create asks the operating system for a block the size of the image, and every IMAQ Dispose hands it back. Inside a loop that runs at frame rate the pair repeats on every scan. A four-camera line at 30 frames per second runs its inspection loop 120 times a second; three Create/Dispose pairs per pass means 360 allocation-and-free cycles every second (120 × 3). Each cycle costs a call into the allocator, and freed blocks do not always line up with the next request, which shows up as jitter in loop time.
There is also the silent failure. One execution branch that skips its Dispose leaks a full image per pass, and the leak stays invisible until the station slows down hours into a shift. When loop time is already close to the acquisition budget, Acquisition faster than processing: fix the frame rate gap shows how the gap widens. The decision is plain: no IMAQ Create and no IMAQ Dispose inside any loop that runs at acquisition rate. If the growth has already shipped, Vision software memory leaks: find and fix inspection loop growth is the diagnosis path.
The trap: a disposed second reference kills the original
Here is the behaviour that breaks multi-VI programs. A subVI calls IMAQ Create with a name the top level already created — 'work', say — and receives a second reference to the top level's buffer, not a new image. Dispose that reference when the subVI ends and the named image itself is removed from memory. The top level's next iteration then fails on an invalid or empty image, usually from the second pass onward, which reads like a timing problem when it is a lifetime problem.
The fix is ownership: one creator and one disposer per buffer, both outside the loop.
The pattern: create once, reuse every iteration, dispose once
Structure the program as an initialization VI, the processing loops, and a shutdown path. The initialization VI creates the whole set and returns the references as a cluster, the loops use them, and the shutdown path disposes them. IMAQ Dispose can close every open image at once when you wire its boolean input for that, which turns shutdown into a single call.
Count the buffers from the pipeline, not from habit:
| Buffer | Role | Created | Disposed |
|---|---|---|---|
| acquire | destination for the newest frame | init | shutdown |
| gray | colour-to-gray intermediate | init | shutdown |
| binary | threshold result | init | shutdown |
| display | resized copy for the viewer | init | shutdown |
| prev | previous frame for change checks | init | shutdown |
A colour-to-gray-to-binary chain needs the original plus two results, so the table carries three processing buffers on its own. A scaled copy for the viewer needs its own buffer, because otherwise the viewer resizes whichever image you wired into it that pass. Change detection adds the previous-frame buffer. A subVI that genuinely needs private scratch space can create it on its first call and hold it in an uninitialized shift register — never dispose it per call.
When you later build the VIs into a stand-alone executable, strip the diagrams only after this shutdown path has run clean. Removing block diagrams from LabVIEW VIs without breaking them explains what breaks and what survives.
Sizing the pool: the arithmetic
Take a four-camera monochrome station at 5 MP. One image is 2,448 × 2,048 pixels at 1 byte per pixel: 2,448 × 2,048 × 1 = 5,013,504 bytes, about 5 MB. Four buffers per camera — acquire, binary, display, prev — give 4 × 5 MB = 20 MB per camera, so the station's pool is 4 × 20 MB = 80 MB. A colour head changes two factors at once: 3 bytes per pixel makes each image about 15 MB, and the gray intermediate adds a fifth buffer, so 5 × 15 MB = 75 MB per camera and 300 MB for the station.
Compute the pool once, write it into the buffer table, and hold it against the controller before you order it. The MV-VC2140-128G40-1T is the compact vision controller for a station of this size, with 8 GB of memory and 3-4 LAN ports. An 80 MB pool takes about 1% of 8 GB — 80 of 8,192 MB — leaving the rest for the operating system, your processing code and the driver.
- LAN ports
- 3-4 LAN ports
- USB 3 ports
- 2 USB 3 ports
- Light outputs
- 4 built-in channels
- Memory
- 8 GB
If the cameras arrive through a frame grabber rather than the controller's own network ports, nothing about the pool changes: the driver stages incoming frames in its own buffers, and your loop pulls from there. The MV-GE1104 is a 4-port GigE frame grabber. Four cameras, one card. On a two-camera bench the pattern is lighter still, and the MV-GE2002P is a dual-port PoE NIC that carries both links.
- Interface
- GigE
- Ports
- 4 ports
- Interface
- GigE
- Ports
- 2 ports
Filter by ports and memory in the vision controllers category, and by interface and port count in the frame grabbers category, where those models are listed.
Passing references through a deep hierarchy
With three or four buffers, wire them through the connector pane; the wires document the flow. Past roughly a dozen references spread across subVIs, the standard LabVIEW answer is a functional global: a non-reentrant VI with an uninitialized shift register that stores the cluster of references. The first call creates the set from the name list, and every later call reads it out. Any VI in the hierarchy can then fetch the 'binary' reference by name, without a dozen wires crossing every connector pane.
Two loops sharing one buffer is the case for locking, and the cheaper decision is not to share. Give each loop its own named buffers, sized by the same arithmetic. When a shared buffer really is unavoidable — one loop draws overlays while another reads results — guard it with a semaphore so a write cannot straddle a read. The lock costs time only where the loops collide, which with separate buffers is never.
When reusing one buffer is the wrong call
Reuse breaks in four cases:
- Logging every frame. The next pass overwrites the buffer, so copy each saved frame into its own slot in a ring. Python frame queue memory leak: bound the queue and reuse frames is the same pattern, with the queue bounded so the ring cannot grow without limit.
- Two consumers, different times. Copy once into per-consumer buffers; otherwise the second consumer reads a half-overwritten image.
- Reentrant subVIs. Instances run concurrently and cannot share a module-level buffer, so each instance creates and owns its own set.
- Acquisition faster than processing. A single acquire buffer ping-pongs with the camera. Two acquire targets let the driver fill one while the loop drains the other.
The ownership rule does not change with language: C# camera memory leak: manage image buffer ownership is the identical decision in a different runtime. More loop-architecture material sits on the systems topic page.
Checks before you commit
- Search every block diagram for IMAQ Create and IMAQ Dispose. Each occurrence belongs in the initialization VI or the shutdown path, and none belongs in a loop, because per-iteration allocation is the cost this whole pattern removes.
- Run a one-hour soak at production frame rate. Process memory should climb to the pool plus your code and then hold flat; a slope that never flattens is a Create without a Dispose, or copies queued faster than the loop drains them.
- Trigger the shutdown path on a live station and start it again. A clean second start with no stale names confirms every buffer was released once and recreated once.
- Keep the name table and the initialization VI in step: every buffer in the table exists exactly once in the code, and no Create anywhere uses a name from the table.
If the pool and the camera count outgrow the controller you run now, shortlist the replacement directly in the vision controllers category, where the filters take LAN-port count, PoE and memory. When you have the model, contact us and we will check the pool sizing against it before you order.
Questions
Do I ever have to call IMAQ Create inside a subVI?
Yes, when the subVI owns a private work buffer. Create it on the first call and hold the reference in an uninitialized shift register, so every later call reuses the same image. What you never do is dispose it at the end of each call — that create-and-dispose pair per invocation is exactly the per-iteration cost this page removes. One exception: a reentrant subVI cannot share a buffer, so each instance creates and owns its own.
How much memory do 10-bit or 12-bit camera images need in the pool?
Unpacked 10- or 12-bit data usually lands in two bytes per pixel, so double the 8-bit figure: 2,448 × 2,048 × 2 = 10,027,008 bytes, about 10 MB per image. Four buffers per camera across four cameras is 4 × 4 × 10 MB = 160 MB. Colour at three bytes per pixel instead triples the 8-bit numbers. Plan the pool from the pixel format you actually acquire, not the sensor's headline bit depth.
Two subVIs both create references with the same image name — what actually happens?
They hold two references to one image, not two images. A pixel written through either reference is visible through the other immediately, and a single IMAQ Dispose on either side removes the image for both. Prevent it by naming every buffer after its role, keeping the name list in one initialization VI, and giving each processing stage its own named buffer when the pixels must not interfere.
Does the transport matter — GigE, USB3 or a frame grabber?
No. Reference lifetime is decided in your LabVIEW code, above the driver. The transport only decides how driver-side buffers stage frames before your loop takes them, and how much bandwidth is available. The create-once, reuse-every-iteration pattern is identical for a GigE camera, a USB3 camera and a Camera Link or CoaXPress line fed through a frame grabber.
How do I prove the reuse works before deployment?
Watch process memory during a soak test. Run the loop at production frame rate for an hour with the pool created once: memory should climb to the pool plus your code and then stay flat. Growth that never plateaus points at a Create without a matching Dispose, or at copies queued faster than the loop drains them. Fix the slope before you build the executable, not after.


