You switch off the setting that should hide the camera image, and the live picture stays on screen exactly as it was. That first switch is almost never the only draw path. In a component-based application the capture initializer, a preview canvas and a skeleton or result overlay can each paint the same stream, and each carries its own display flag, so the others keep drawing after you disable one. What follows is the layer map, four tests that identify which component owns the picture you see, and the fix that clears the screen while acquisition keeps running. The same diagnosis applies whether the source is an area scan camera on a GigE link, a smart camera inspecting on the device, or a vision controller feeding several displays at once.
Symptom: the first switch you find changes nothing
The report takes a familiar shape. You disable the show_image flag on the capture initializer, the one the getting-started walkthrough names, and the live image keeps updating. You hide the main preview component and a full-motion camera picture remains in the viewport. Two details point at the cause. The image still updates, so something is drawing it every frame. And the project often contains two overlay components whose display flags are spelled almost identically, one capitalized and one not, because the drawing code was copied between them. Copy-pasted draw code is where a second consumer hides, and a near-duplicate flag name is its fingerprint.
A preview that never appeared at all is a different fault: how the camera live preview in GUI applications is wired decides what registers as a consumer in the first place.
Three layers that can hold the image open
A camera frame on its way to a window passes through up to three layers, and any of them can own a draw call.
- Capture layer. The initializer or acquisition object starts the stream and often renders its own first preview. Its show flag usually controls that one object's drawing and nothing else.
-
Consumer layer. Every visualizer, such as a skeleton overlay, a result overlay or a measurement overlay, subscribes to the same stream and paints its own copy of the camera texture. Each carries a flag named something like
show_canvas. - Window layer. The viewport itself can hold a background surface, and the compositor decides the stacking order. Sizing and placement are covered in control vision display window size and screen position.
The frame is a shared texture, not a picture with a single owner. Each consumer holds a reference and draws it once per frame, so removing one consumer removes one draw call while the picture stays until the last drawer stops. Annotation overlays are consumers too: the same draw path that paints the graphics for a circular ROI overlay with a numeric radius paints the camera texture behind them, and the pattern matching overlay not showing case is the same layer seen from the other side.
Likely causes, most probable first
Work down this list; the first two cover most cases.
- A second consumer still draws it. Each visualizer and the preview canvas hold their own flags. Test: disable each component's own draw flag one at a time; the screen clears when the last drawer stops.
- The flag you toggled belongs to the wrong object. The initializer's flag and the visualizers' flags carry near-identical names. Test: check which object owns the property you changed, then list the display flags on every visualizer.
- The draw happens in code, not behind a flag. A line in the render loop blits the texture with no switch at all. Test: comment out the suspected call; if the image survives, another call exists.
- The picture is frozen, not drawn. A stalled or paused stream leaves the last frame in the framebuffer. Test: cover the lens; no change means frozen, not live.
- The flag gates capture, not display. Toggling it stops your inspection results instead of the image. Test: watch the result counter while you toggle.
Four tests that find every drawer
Freeze test. Cover the lens with your hand. An image that keeps changing proves a live draw path; a static picture means a stalled stream, and no display flag will fix that.
Subscription count. Search the project for the camera or stream name. Every hit that stores the texture or registers a frame callback is a consumer. Then search component names for canvas, overlay, preview and visualizer, and write the list down.
Draw-call count. Open the renderer's statistics or frame-debugger view and count how many times the camera texture is bound per frame. One binding per consumer is the healthy pattern; two bindings while only one preview is visible means a hidden second consumer.
Mark the stream. Set the stream to a distinctive resolution, or switch mono to color for one run. Every surface that changes with it is a consumer, including windows outside the main viewport.
The fix: clear every consumer's own draw flag
The decision: disable the per-component draw flag on every visualizer and on the preview canvas, and leave the capture initializer enabled. Touch the initializer's show flag only after a test proves it controls that object's own preview and nothing else.
The change is a short list of settings. The names below are illustrative; your SDK calls them something else, but the shape is the same:
camera_stream_enabled: bool = True # acquisition stays on
preview_canvas.show_image: bool = False # first draw path off
skeleton_overlay.show_canvas: bool = False # second draw path off
result_overlay.show_canvas: bool = False # third draw path off
Prove the result before closing the ticket. Run ten seconds with the lens covered, then ten with it uncovered, while logging the received frame counter. The screen must stay dark in both runs and the counter must keep climbing at the camera's frame rate, which shows display is off while acquisition is alive. If disabling a flag also stops the inspection results, that flag was feeding the pipeline: re-enable it and look for the display switch on the component that actually draws.
What a forgotten draw path costs
Even when the stubborn image is only an annoyance, the extra consumer does real work. Take a 60 fps camera delivering 1920 × 1080 in color at 8 bits per channel. One full copy is 1920 × 1080 × 3 bytes = 6,220,800 bytes, about 6.2 MB. Two consumers that each copy every frame move 12.4 MB per frame, and at 60 fps that is about 746 MB/s of memory traffic before any drawing starts.
Time is just as tight. The frame budget is 1000 ms / 60 = 16.7 ms, and a draw pass costing 4-6 ms on a shared interface thread takes 8-12 ms with two consumers, up to about 70 percent of the budget. That is the stutter and tearing behind a double-drawn preview, and it is why overlays that only annotate should draw on a shared canvas instead of each carrying the camera texture, as laid out in update overlay graphics every frame at full preview rate.
Run the same arithmetic on your stream: a 1280 × 1024 mono camera at 30 fps gives one consumer 1280 × 1024 × 1 byte = 1,310,720 bytes, about 1.3 MB per copy and about 39 MB/s, which a second consumer doubles to about 78 MB/s. Small for the memory bus, but still one more full draw pass per frame on the interface thread.
When the obvious fix is the wrong one
Three situations look the same on screen and need different actions.
Frozen last frame. Nothing is drawing, so display flags change nothing. Clear the canvas or restart the stream. If the surface is gray or black instead of a picture, that is a pixel format problem; the camera preview shows a gray image: fix the format mismatch case covers it.
A show flag that also gates capture. Some initializer flags decide whether frames are pushed downstream at all. Turning one off stops the inspection and leaves the screen dark for the wrong reason. Toggle it and watch the result counter: results stopping means the flag feeds the pipeline, so re-enable it and use the per-consumer flags instead.
A service that should never draw. A headless acquisition process can still create a visible surface by default. Run it in off-screen or service mode instead of muting a window that should not exist.
Prevention: one owner per pixel
Three habits keep this bug class out of your code.
One visible owner. Pick the single component that may show the camera image. Every other component draws annotations only, never the texture behind them, and its flag name says so.
Names that describe the drawing. A flag called show_canvas on a skeleton overlay documents itself. A generic show_image on the capture initializer invites the confusion described above. Two almost-identical flag names in two components are a warning that the draw path is duplicated.
One consumer list. Register every frame consumer at startup and log the list. When the picture must disappear, iterate the list and mute each entry; the log tells you how many flags to expect. With several streams in one application, the multi camera system design for reliable image capture structure keeps each camera's consumers grouped, and what each view really needs to show is the machine vision image display ROI design question to settle before the second consumer is added.
Two-minute checklist
- Cover the lens: live draw or frozen frame?
- List every component that stores the texture or registers a frame callback.
- Toggle the initializer's flag once and watch the result counter: does it gate display or capture?
- Disable each consumer's own draw flag, one at a time, checking the screen after each.
- Leave the capture initializer enabled.
- Log the received frame counter for ten seconds to prove acquisition survived.
- Name every display flag after the component and the surface it draws.
Clear the consumers, keep the stream, and the screen goes dark for the right reason. When the hardware side needs a second look instead, the camera finder filters area scan and smart models by interface and resolution, the camera configurator walks a full set-up step by step, and the systems topic page collects the other display and preview notes.
Questions
Why do two components carry flags with almost the same name?
Because the drawing code was copied from one component to the other, and each copy brought its own display flag. The duplication is a symptom, not the cause: both components subscribe to the camera stream independently. Fix the draw paths first, then rename the flags so the next reader sees two consumers where the code used to show one.
Can two components share one camera frame without copying it twice?
Yes, when both only read. Hand each consumer a reference to the same immutable frame and let each draw its annotation on top; one writer with many readers is safe. Copy only when a consumer must modify pixels. Two owners that each copy the full frame every scan double the memory bandwidth for no visual gain.
Does hiding the preview reduce the camera frame rate?
Not by itself: display sits downstream of acquisition, so muting the draw paths leaves the delivered frame rate unchanged. The exception is a draw path that shares a thread with acquisition or holds a locked buffer, which shows up as dropped frames. Compare the camera's delivered frame-rate counter before and after the change to catch that case.
Why does the image come back in the deployed build after I cleared it?
A component whose draw flag defaults to on starts painting the moment it spawns, so a visualizer created late in startup can resurrect the image even though your initialization code ran. Set every display flag to off in the component's default state, not only during startup, so nothing can spawn with drawing enabled.
In what order should I disable the layers?
Top of the draw stack first: the overlay visualizers, then the main preview canvas, then the window background. Check the screen after each step so you learn which component was drawing. Leave the capture initializer enabled; if turning something off also stops the inspection results, that layer was feeding the pipeline, so re-enable it and keep looking for the display switch.