Cameras & sensorsHow-to

GenTL producer in a Python venv on Linux: install order

Why the GenTL producer installs system-wide while the Python binding lives in the venv, the four install steps on Linux, and the check that proves it works.

LumaSpec Vision10 min read Share on LinkedIn

On this page 11 sections
  1. 01A venv isolates packages, not drivers
  2. 02The install order
  3. 03Step 1: install the producer system-wide
  4. 04Step 2: create and activate the venv
  5. 05Step 3: pip install the binding inside the venv
  6. 06Step 4: make the .cti visible
  7. 07Worked example: Ubuntu 22.04, Python 3.10, one GigE camera
  8. 08Cameras this applies to
  9. 09Common mistakes, in order of how often they bite
  10. 10When a plain install beats a venv
  11. 11Questions

You create the venv, pip install the camera binding, run the enumeration sample — and the camera list comes back empty. The cause is almost never your code. A Python virtual environment isolates pip packages, not system drivers: the GenTL producer that actually talks to the camera is a native library, installed system-wide, and the venv neither contains it nor hides it. This guide gives the install order that works on Linux — producer with apt first, a fresh shell second, the venv third, binding and path check fourth — plus the one-command test that proves the camera is visible before you write any inspection code. It applies to any GigE Vision or USB3 Vision camera driven through a GenTL consumer, on a desktop, in a container, or on an embedded Linux board.

A venv isolates packages, not drivers

Four layers sit between your script and the sensor:

  1. your Python code, on the interpreter the venv points at;
  2. the camera binding, a pip package inside the venv's site-packages;
  3. the GenTL producer — the native shared library with the .cti extension that your camera maker ships as a system package;
  4. the transport driver in the kernel: the GigE Vision packet filter or the USB3 Vision host driver.

venv governs layers 1 and 2 only. Python's documentation defines a virtual environment as an environment with its own independent set of installed packages; nothing in that definition reaches the producer or the driver. apt installs into /usr and /opt, which every venv on the machine shares. So the system-wide producer install is not a workaround: the GenICam GenTL standard makes consumers discover producers through the GENICAM_GENTL64_PATH environment variable for 64-bit producers (GENICAM_GENTL32_PATH for 32-bit), and that variable points at a directory the whole machine reads.

The install order

Four steps, and the order is the whole trick:

  1. Install the GenTL producer system-wide from the camera maker's package (.deb on Debian and Ubuntu, .rpm on Fedora or RHEL-type systems).
  2. Open a fresh terminal so the installer's profile script exports GENICAM_GENTL64_PATH.
  3. Create and activate the venv with python3 -m venv.
  4. Inside the active venv, pip install the camera binding, confirm the path variable, and enumerate.

Step 1 must happen before the enumeration in step 4. Steps 3 and 4 can technically run first — nothing breaks until you enumerate — but doing the producer first means every later shell already carries the correct path.

Step 1: install the producer system-wide

Download the GenTL producer from your camera maker's support page — it is the SDK component named as a producer, not the Python package. On Debian or Ubuntu, install the local .deb directly:

cd ~/Downloads
sudo apt install ./<producer-package>.deb

apt resolves the dependencies and copies the .cti plus its support libraries into system paths. On Fedora, RHEL-type or openSUSE systems, the equivalents are dnf install ./<producer-package>.rpm or zypper install ./<producer-package>.rpm.

Most installers drop a script under /etc/profile.d that exports GENICAM_GENTL64_PATH at login. Open a new terminal and check:

echo "$GENICAM_GENTL64_PATH"

If the producer itself then fails to load in a Linux application — a different fault from an empty path — the GenICam producer loading errors article walks that failure.

Step 2: create and activate the venv

Pick one location outside any single project if several camera projects will share it. The target directory must not already exist — venv creates it:

python3 -m venv ~/venvs/cameras
source ~/venvs/cameras/bin/activate

The prompt now carries the environment name, and python and pip resolve to the venv's own interpreters. venv ships in the Python standard library since Python 3.3, so any distribution from around 2014 onward needs no extra package. The venv module reference in the official Python documentation lists every flag, and the packaging user guide on packaging.python.org walks the same create-activate-install cycle in more depth.

If your interpreters come from pyenv rather than the system, the venv mechanics are identical — only the install-script pitfalls differ, and the pyenv pip fix covers those.

Step 3: pip install the binding inside the venv

With the venv active, install only the Python binding — the pip package your camera maker publishes for GenICam access:

python -m pip install <camera-binding>

Use python -m pip rather than bare pip so the package lands in the interpreter you will actually run. The binding is a thin wrapper: it ships no driver logic, so it cannot substitute for the producer. If a GUI sample program is on your list, install its windowing dependencies system-wide; Qt6-based samples on Debian and Ubuntu usually need:

sudo apt install libxcb-cursor0

That belongs outside the venv, like everything here that is not pure Python.

Step 4: make the .cti visible

In the normal case this step is a no-op: the profile script from step 1 already exports the variable for every shell. Two cases need action.

If the variable is empty, find the producer and export its directory in the active venv:

find /opt /usr/lib -name '*.cti' 2>/dev/null
export GENICAM_GENTL64_PATH=$(dirname "$(find /opt /usr/lib -name '*.cti' 2>/dev/null | head -n1)")

The one-liner suits a bench machine with a single producer. With several camera makers installed, export the directory of the one you use. To make it permanent for this venv, append the export line to the end of ~/venvs/cameras/bin/activate — the activate file is an ordinary shell script, so the variable is set on every activation.

If your consumer takes an explicit .cti path instead of using the variable, skip the export but mind Python string escaping on the path; that fault has its own write-up at CTI file path errors in Python. A 32-bit consumer reads GENICAM_GENTL32_PATH instead — on current hardware you are building 64-bit, so GENICAM_GENTL64_PATH is the one that matters.

Worked example: Ubuntu 22.04, Python 3.10, one GigE camera

The inputs, and where each comes from:

  • OS: Ubuntu 22.04, whose default python3 is 3.10 — new enough for venv, which is in the standard library since 3.3.
  • Camera: one GigE Vision camera on the network — in this example the MV-CU020-90GC, a 2.5 MP GigE model.
  • Network link: a 1 Gb/s NIC on a dedicated interface, with the frame size at the 1500-byte default or raised to a 9000-byte jumbo frame where the switch supports it.
  • Producer: the camera maker's GenTL producer .deb, downloaded to ~/Downloads.
  • venv: ~/venvs/cameras — this example's own choice of location.
  • Binding: the pip name from the maker's SDK install page, written here as <camera-binding>.

The rule in words: producer system-wide before any enumeration, binding only inside the venv, path variable resolving to the producer's directory before the first enumeration.

The sequence, with a check after each stage:

# stage 1: producer, system-wide
cd ~/Downloads
sudo apt install ./<producer-package>.deb

# stage 2: fresh terminal, then venv and binding
python3 -m venv ~/venvs/cameras
source ~/venvs/cameras/bin/activate
python -m pip install <camera-binding>

# stage 3: the proof
echo "$GENICAM_GENTL64_PATH"
find /opt /usr/lib -name '*.cti' 2>/dev/null

Expected results: echo prints one directory; find prints one .cti under it; and the binding's first-enumeration sample lists one interface of type GigE Vision with the camera's device ID beneath it, typically within a 3 s discovery window on a healthy link. That output is the test that proves the order worked — none of your inspection code has run yet.

The decision: if echo prints empty, the shell predates the install — re-login and rerun. If it prints a path but find shows no .cti there, the producer package did not install for this architecture (a 64-bit consumer needs the 64-bit producer) — reinstall before touching code. An empty camera list with a correct path is a different fault; the empty-camera-list article takes it from there.

Cameras this applies to

Any camera a GenICam consumer drives follows this flow — GigE Vision, USB3 Vision and CoaXPress all reach your binding through a producer. In the shop that means the area scan cameras for bench and line work and the board-level cameras for building into your own Linux hardware; the board-level modules are the natural fit for headless venv deployments. Three picks spanning the interfaces:

Area scan camerasHikrobot MV-CU020-90GC 2.5 MP Area Scan Camera, GigE, GMAX4002, ColorMV-CU020-90GC
Sensor format
1/1.7"
Lens mount
C-mount
$309.00View part
Area scan camerasHikrobot MV-CT0MG0060/R/NN/NN 6 MP Area Scan Camera, 3rd Gen., GigE, Mono, BASEMV-CT0MG0060/R/NN/NN
Sensor format
1/1.8"
Lens mount
C-mount
$909.00View part
Board-level camerasHikrobot MV-CB016-10GM-M-W 1.6 MP GigE board-level camera, IMX296, Mono, M12 lens mount, WTB GigE portMV-CB016-10GM-M-W
Sensor format
1/2.9"
Lens mount
M12
$559.00View part

All three enumerate through a producer exactly as in the worked example; the interface line on each card is the spec that matters here.

Common mistakes, in order of how often they bite

  1. Expecting pip to bring the producer. pip installs Python into site-packages; the producer is native code from a system package. An empty list right after a fresh pip install points back at step 1.
  2. Enumerating from the shell that installed the producer. The profile script runs at login, not during package install. One echo of the variable tells you; if it is empty, re-login.
  3. A path that points at nothing. The variable is set but the .cti is elsewhere — usually a manual export done from memory. Rerun the find one-liner from step 4.
  4. Escaping faults in an explicit .cti path. Backslashes or quotes inside a Python string break the load; the fix is the one described in step 4 above.
  5. Windows parity assumptions. Same layering, different locations: the producer DLL under the SDK's Program Files folder, the variable set by the installer. The Windows DLL path article covers that failure.
  6. Blaming the venv for a slow camera. Enumeration fine but frames stuck at 30 fps is a network-link suspect, not Python — see the 30 fps GenTL loop article.
  7. Leaked handles after a crashed run. A script that aborts mid-acquisition and leaves the consumer open makes the next run behave oddly; the handle cleanup article shows the close-out pattern.

When a plain install beats a venv

A venv earns its keep when several projects need different binding versions on one machine. Three cases where it does not:

  • One bench script, one camera: install the binding with system pip and skip steps 2 and 3 — the producer ordering still applies.
  • A container image: producer in an apt layer, binding in a later pip layer, GENICAM_GENTL64_PATH set with an ENV instruction — the same order without activation.
  • A shipping appliance running one application: a single system install keeps updates to one package operation.

When you are choosing hardware for the stack, the camera configurator filters by interface and resolution, and the cameras topic page gathers the other GenTL set-up articles. If you want a second pair of eyes on an install that still will not enumerate, the /contact/ page reaches the applications team — send the producer package name, the distribution, and the output of the two checks from the worked example.

Questions

Can I pip install the GenTL producer into the venv?

No — the producer is native code delivered in the camera maker's system package (.deb or .rpm). pip installs Python into the venv's site-packages only; it cannot copy a .cti into system paths or set GENICAM_GENTL64_PATH. A few SDK wheels bundle their own producer, but that ties the camera to one vendor's stack and hides the driver from every other GenTL consumer on the machine. Install the maker's package and any consumer can enumerate the camera.

Do I need sudo for the producer install?

Yes for the producer package: apt, dnf and zypper need root to copy the .cti into system paths and drop the profile script into /etc/profile.d. Everything after that runs as your own user — creating the venv, activating it and pip installing the binding need no root at all. That split is the point of the design: privileged once at driver install, unprivileged for every code change afterwards.

Does this change for a CoaXPress camera?

Yes, it carries over. CoaXPress cameras are GenICam devices like the rest: the frame grabber's driver package ships a GenTL producer, and your binding enumerates it the same way. The order holds — grabber driver system-wide first, fresh shell, venv, binding, then confirm the path variable — and the producer file is still a .cti. Take the package name from the grabber maker's install page rather than assuming it matches the camera's.

Does this change for USB3 Vision cameras?

No. The transport differs — a USB3 Vision camera uses a USB3 Vision producer instead of a GigE Vision one — but the install order is identical: system package first, fresh shell, venv, binding, path check. One extra step is specific to USB: install the udev rules from the producer package, or a non-root user sees no device at all and enumeration returns empty even though everything else is correct.

What if the venv was created before the producer was installed?

Nothing needs redoing. A venv records no producer state at creation time — it is just a directory of packages. Install the producer whenever it suits you, open a fresh terminal so the profile script runs, activate the venv and enumerate. Only the environment of shells opened before the install is stale, and one re-login or a manual export fixes that.