Commit 0010e4e
Jetson dockerfiles: OpenCV with GStreamer on JP62, add BuildKit cache mounts on JP62/JP71, bump JP51 OpenCV (#2321)
* Build OpenCV from source with GStreamer + bake NVIDIA gst plugins into JP62
The pip opencv-python wheel ships without GStreamer, and the l4t-cuda runtime
base used for the JP62 image has no multimedia stack, so cv2.VideoCapture on
Jetson silently falls through to the plain v4l2 ioctl path -- pulling raw 4K
YUYV, doing CPU YUV->BGR, and never reaching NVIDIA's hardware engines.
Three pieces, all needed together:
1. Compile OpenCV from source in the builder stage with WITH_GSTREAMER=ON,
WITH_FFMPEG=ON, WITH_LIBV4L=ON, WITH_CUDA=ON. Replaces the pip-installed
opencv-python/opencv-contrib-python with a wheel built from python_loader.
Adds a build-time cv2.getBuildInformation() check so a regression in the
detected flags fails the build instead of silently shipping CPU-only cv2.
2. Add the GStreamer runtime + PyGObject stack to the runtime stage
(libgstreamer1.0-0, plugins-base/good, python3-gi, gir typelibs) so that
downstream Python code using "from gi.repository import Gst" to construct
hardware capture/encode pipelines can import and run inside the container.
3. Copy NVIDIA's GStreamer plugins (libgstnv*.so) plus the tegra runtime libs
from the JetPack builder stage into the runtime image, instead of relying
on nvidia-container-passthrough host mounts. Self-contained -- works
without any host-side changes deployed.
Branched off v1.2.7 to keep the diff minimal.
* Pass CMAKE_POLICY_VERSION_MINIMUM=3.5 to OpenCV cmake
CMake 4.x removed compatibility with cmake_minimum_required(VERSION <3.5).
OpenCV 4.10.0's OpenCVGenPkgconfig.cmake still uses the old declaration,
so the configure step aborts with:
CMake Error at .../OpenCVGenPkgconfig.cmake:113 (cmake_minimum_required):
Compatibility with CMake < 3.5 has been removed from CMake.
Same workaround the existing onnxruntime build step uses.
* Bump OpenCV to 4.13.0 and reorder JP62 build
OpenCV 4.13.0 (released 2025-12-31) is the current latest stable; it builds
cleanly under CMake 4.x without the cmake_minimum_required workaround the
4.10.0 source needed. Aligns both Jetson images.
JP62-specific:
- Move the from-source OpenCV step to AFTER the inference packages install
so the cv2 we install (built with GStreamer + FFmpeg) is the last thing
to touch /usr/local/lib/python3.10/dist-packages/cv2/. Previously the
inference_core/cli/sdk pip install would refetch opencv-python (as a
transitive dep) and clobber our from-source cv2.
- Drop CMAKE_POLICY_VERSION_MINIMUM=3.5 -- not needed on 4.13.0.
* Drop the bake-in attempt for NVIDIA gst plugins
The l4t-jetpack:r36.4.0 builder image does not ship the tegra runtime
libs as a regular populated /usr/lib/aarch64-linux-gnu/tegra directory.
Those libs are tied to the host JetPack BSP and only appear inside the
container at run time via nvidia-container-runtime's CSV-driven host
mounts. So `COPY --from=builder /usr/lib/aarch64-linux-gnu/tegra ...`
errors during the build with "not found".
This image now scopes to enabling the cv2 side of the GStreamer stack
(WITH_GSTREAMER=ON OpenCV, python-gi, base gstreamer runtime + GIR
typelibs). The host system has to provide the NVIDIA plugins for any
nv* pipeline element to resolve at run time.
* mkdir tegra/ at end of builder so libnvdla glob COPY can lstat parent
BuildKit needs to lstat the source parent dir before evaluating a glob
COPY pattern, even when "no match" is allowed. The libnvdla fix in
PR #2201 copies tegra/libnvdla_compiler.so*, but l4t-jetpack:r36.4.0
doesn't always ship /usr/lib/aarch64-linux-gnu/tegra/ as a real
directory -- that path is normally populated by nvidia-container-runtime
at container start. Recent builds intermittently fail at this step
depending on layer cache state.
mkdir -p the path so BuildKit can process the COPY. The glob will copy
the real file if it's there, or silently no-op if not. Doesn't paper
over the underlying flakiness of the libnvdla source path, but lets
this PR's build progress.
* Correct libnvdla_compiler.so source path from tegra/ to nvidia/
The actual location in nvcr.io/nvidia/l4t-jetpack:r36.4.0 is
/usr/lib/aarch64-linux-gnu/nvidia/libnvdla_compiler.so, not tegra/.
The original COPY in PR #2201 used the wrong path; the glob silently
matched zero files, so the libnvdla fix shipped an empty layer and
ONNX Runtime kept falling back to CPU on Jetson 6.x.
Same one-line correction as PR #2306. Bundling it here so this PR's
build can also actually ship libnvdla_compiler.so.
* Set OpenCV python install path so the loader stub finds cv2 at runtime
Without OPENCV_PYTHON3_INSTALL_PATH / PYTHON3_INCLUDE_DIR / PYTHON_VERSION
the python_loader config.py bakes the BUILDER path
(/build/opencv/<ver>/release/lib/python3) into sys.path. That path only
exists in the builder stage, so in the runtime image the loader stub
fails to find the native cv2 .so, falls back to importlib.import_module("cv2")
which re-enters the same stub package, and Python raises:
ImportError: ERROR: recursion is detected during loading of "cv2"
binary extensions. Check OpenCV installation.
JP51's PR #2100 passes these flags explicitly; replicating the same set
here so the loader references /usr/local/lib/python3.10/dist-packages.
* Rescue install-tree cv2 config files; parameterize for porting
Two related changes to make the from-source OpenCV build correct and
reusable across jp5.1 / jp6.2 / jp7.1:
1. Rescue the install-tree config files. After ninja install, the files
at ${INSTALL_PATH}/cv2/config*.py have install-tree paths (correct).
The pip install of the wheel built from /build/.../python_loader then
overwrites them with BUILD-TREE config files (paths like
/build/opencv/opencv-X.Y.Z/release/lib/python3) which only exist in
the builder stage. In a multi-stage runtime image those paths are
dead, so the loader's bootstrap can't find the native cv2 .so,
re-imports the stub package, and Python raises:
ImportError: recursion is detected during loading of "cv2"
Fix by saving config*.py between ninja install and pip wheel/install,
restoring after.
2. Pull per-platform variables out of the cmake invocation into ARGs at
the top of the builder (OPENCV_CUDA_ARCH, OPENCV_PYTHON_INSTALL_PATH,
OPENCV_PYTHON_INCLUDE_DIR, OPENCV_PYTHON_VERSION). Replicating this
OpenCV block in jp5.1.1 / jp7.1.0 dockerfiles is now a copy-paste
plus four ARG value changes; no surgery inside the cmake invocation.
Adds a build-time sanity check that simulates the runtime stage by
stripping /build/ from sys.path before importing cv2. If the install-
tree rescue ever regresses, the docker build fails here -- not 90
minutes later on the device.
* Add BuildKit cache mounts to Jetson builds and split Depot projects
Pins syntax to dockerfile:1.7 on both jp62 and jp71 so ARG interpolation
in cache-mount ids works. Adds apt cache mounts (sharing=locked, id scoped
per JetPack + builder/runtime + arch) to every apt RUN so .debs and lists
persist across builds. Disables docker-clean and turns on
Keep-Downloaded-Packages so the apt cache is actually used. Drops the
trailing rm of /var/lib/apt/lists/* since that path is now the cache mount.
Adds per-build-tree cache mounts on the expensive C++ compiles:
PyTorch, torchvision, onnxruntime (build/cuda12 on jp62, build/Linux on
jp71), OpenCV (release/ on jp62), with the TRT .deb download also cached
on jp62. Each id is scoped by version + arch + SM so a version bump
starts clean instead of reusing a stale build tree.
The OpenCV step on jp62 stays positioned last in the builder because the
inference wheel installs upstream are not --no-deps and an earlier
opencv-python pull would clobber the from-source build. The build-dir
cache mount makes the forced rebuild after COPY-invalidation a fast
ninja no-op rather than a 30-60 min recompile.
Points jp62 and jp71 workflows at the new dedicated Depot projects
(2rp7mfjw7q for jp62, v1xzfwkc4b for jp71) so they no longer share an
NVMe cache pool and evict each other.
* Split git-clone out of cache-mounted build RUNs
The cache mount on build/ auto-creates its parent directory before the
RUN body executes, so a combined 'git clone ... && cd ... && make' fails
with 'destination path already exists and is not an empty directory'.
First jp62 attempt with these cache mounts failed at the PyTorch step:
fatal: destination path 'pytorch' already exists and is not an empty
directory.
Split each of PyTorch / torchvision / onnxruntime on jp62 and jp71 into
two RUNs: a clone RUN with no mount, then a build RUN whose cache mount
overlays build/ on the cloned source layer.
* Bump all GitHub Actions to latest major versions
Bumps:
- actions/cache v3 -> v5
- actions/checkout v2/v3/v4 -> v6
- actions/create-github-app-token v1 -> v3
- actions/setup-node v3 -> v6
- actions/setup-python v2/v5 -> v6
- actions/upload-artifact v4 -> v7
- aws-actions/amazon-ecr-login v1 -> v2
- aws-actions/configure-aws-credentials v2 -> v6
- dcarbone/install-jq-action v2.1.0 -> v3.2.0
- digicert/ssm-code-signing v1.0.0 -> v1.2.1
- docker/login-action v2/v3 -> v4
- docker/setup-buildx-action v2 -> v4
- docker/setup-qemu-action v2 -> v4
- google-github-actions/auth v2 -> v3
- google-github-actions/setup-gcloud v2 -> v3
depot/* already pinned to floating v1 (latest major).
actions/upload-release-asset and pypa/gh-action-pypi-publish kept
on their existing pins (v1 already latest; pypi-publish uses the
release/v1 stable-channel ref recommended by upstream).
* Bump jp71 flash-attn MAX_JOBS from 1 to 2
PR #2068 reduced this to 1 because MAX_JOBS=4 was starving the Depot
BuildKit gRPC connection during flash-attn CUDA kernel compilation.
2 is a middle ground: flash-attn's nvcc instances peak ~3-4 GB each
against PyTorch 2.10 headers, so 2 concurrent fits comfortably on a
32 GB arm builder while halving the wall time vs serial.
With the cache mounts from c5701f2 in place, the cost of a regression
here is ~30-60 min of flash-attn rebuild, not the full 4hr build.
jp62 stays at MAX_JOBS=4 because it's on PyTorch 2.6 (lighter headers)
and that build is already known-good.
---------
Co-authored-by: Paweł Pęczek <146137186+PawelPeczek-Roboflow@users.noreply.github.com>1 parent 16ba9a0 commit 0010e4e
62 files changed
Lines changed: 398 additions & 230 deletions
File tree
- .github/workflows
- docker/dockerfiles
- inference_models/dockerfiles
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | | - | |
| 23 | + | |
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
27 | | - | |
| 27 | + | |
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
| |||
180 | 180 | | |
181 | 181 | | |
182 | 182 | | |
183 | | - | |
| 183 | + | |
184 | 184 | | |
185 | 185 | | |
186 | 186 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | | - | |
| 23 | + | |
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| |||
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
82 | | - | |
| 82 | + | |
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| |||
328 | 328 | | |
329 | 329 | | |
330 | 330 | | |
331 | | - | |
| 331 | + | |
332 | 332 | | |
333 | 333 | | |
334 | 334 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | | - | |
| 23 | + | |
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| |||
132 | 132 | | |
133 | 133 | | |
134 | 134 | | |
135 | | - | |
| 135 | + | |
136 | 136 | | |
137 | 137 | | |
138 | 138 | | |
| |||
381 | 381 | | |
382 | 382 | | |
383 | 383 | | |
384 | | - | |
| 384 | + | |
385 | 385 | | |
386 | 386 | | |
387 | 387 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | | - | |
| 16 | + | |
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | | - | |
| 23 | + | |
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
27 | | - | |
| 27 | + | |
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
| 34 | + | |
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | | - | |
| 29 | + | |
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
| 34 | + | |
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
30 | | - | |
| 30 | + | |
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
35 | | - | |
| 35 | + | |
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
35 | | - | |
| 35 | + | |
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| |||
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
55 | | - | |
| 55 | + | |
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
62 | | - | |
| 62 | + | |
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | | - | |
| 29 | + | |
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
| 34 | + | |
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | | - | |
| 29 | + | |
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
| 34 | + | |
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| |||
0 commit comments